﻿#include <QPainter>
#include <QDebug>
#include <QMouseEvent>
#include "EagleApi.h"
#include "EagleDeviceMgr.h"
#include <QTimer>
#include <memory>
#include <QStyleOption>
#include <QDir>
#include "QFile"
#include <fstream>
#include "qdisplaywidget.h"
#include "gtBMP.h"
#include "QMenu"
#include "tipDlgs.h"
#include "statisticsanalyze.h"
#include <thread>
#include <future>  // for std::future
#include <chrono>  // for sleep N
#include "opencv2/opencv.hpp"
#include "qsizepolicy.h"
#include "qguiapplication.h"
#include <QScreen>
#include <QResizeEvent>
#include <QScrollBar>
#include <QtGlobal>
#include "user_define/CLSimulation.h"

// 缓冲队列大小 [10/24/2016 ejitec]
//static int MAX_DISPLAY_QUEUE_LEN = 20;
#ifndef WIN64
static unsigned long long MAX_DISPLAY_MEN_SIZE = 1250 * 1024 * 1024; // 单窗口 内存申请 最大 1.7G
#endif

static QString export_type_str[EExportMax] = { "bmp", "Jpeg", "Tiff", "Raw", "Avi", "RawStream1", "bmp" };

QDisPlayWidget::QDisPlayWidget(QWidget *parent, QString name)
: QWidget(parent)
, m_bFullScreen(false)
, m_Image(1366, 768, QImage::Format_RGB888)
, m_startPt(0, 0)
, m_endPt(0, 0)
, m_strNam(name)
, m_tOPCtx(nullptr)
, m_bRecPlay(false)
, m_bRecPause(false)
, m_bRealPlay(false)
, m_bSimuPlay(false)
, m_bInCenter(false)
, m_bTheadAlive(false)
, m_bChecking(false)
, m_Worker(this)
, m_bBitChange(false)
, m_bExporting(false)
, is_display_mode_changed(false)
, m_bHaveReserveThread(false)
, m_UiTask(this)
, m_ExpParms(0,0,0)
{
	m_Image.fill(Qt::black);
	//setToolTip(m_strNam);

#if 0

	QPalette palette;
	// QColor(192, 253, 123)
	palette.setColor(QPalette::Background, Qt::black);
	//setMouseTracking(true);
	//palette.setBrush(QPalette::Background, QBrush(QPixmap(":/res/image/back.jpg")));
	//setPalette(palette);
	setAutoFillBackground(true);

#endif
	//	this->setStyleSheet(QString("QWidget{border-image: url(:/res/image/NoVideo_3148800.jpg)};")); // 效率比较高 但比 style 略差
	// 用背景图片填充 不需要写入默认图片 [11/1/2016 ejitec]
	//m_cQueenData.write(&(StaticFrameMgr::getInstance()->GetDefaultData()));  //申请空间时 指针会自动替换 不用担心 buff长短
	// 处理事件 的线程 响应函数
	m_ptJobHander = new EvtThreadHandler(this, [=](const TCbJob& tcb)
	{
		on_ThreadFinish(tcb);  // done func
	});

	status_dlg = new recExpStatDlg(this, m_strNam);
	status_dlg->hide();

	simu_dlg = new simuStatDlg(this);
	simu_dlg->hide();

	m_Worker.moveToThread(&m_Thread);
	m_Thread.start(QThread::HighestPriority); // 
	connect(this, &QDisPlayWidget::evt_recReq, &m_Worker, &StreamWorker::on_DoStreamRecReq);
	connect(this, &QDisPlayWidget::evt_realReq, &m_Worker, &StreamWorker::on_DoStreamRealReq);
	connect(this, &QDisPlayWidget::evt_simuReq, &m_Worker, &StreamWorker::on_DoSimuRealReq);

	connect(&m_Worker, &StreamWorker::evt_workFinish, this, [=]
	{
		m_bTheadAlive = false;
	});

	connect(&m_Worker, &StreamWorker::evt_refresh, this, [&]()
	{
		update();
	});

	_decoder_name = QString::fromStdWString(DecoderPluginMgr::getInstance()->getDefaultDecoderName());
	_format_name = QString::fromStdWString(DecoderPluginMgr::getInstance()->getDefaultReadFormatName());
	
	QTimer::singleShot(2 * 1000, this, [this]()
	{
		update();
	});


	UpDownIsReservsed = EagleConfigFile::getInstance()->GetDeviceCameraUpDownReverse(DevID(), CCDID());
	LeftRightIsReservsed = EagleConfigFile::getInstance()->GetDeviceCameraLeftRightReverse(DevID(), CCDID());
	
	qDebug() << "QDisPlayWidget:" << m_strNam << ":" << QThread::currentThread();

	m_pAsyncRecExping = new QFutureWatcher<QVariant>(this);

	//
	connect(m_pAsyncRecExping, SIGNAL(resultReadyAt(int)), this, SLOT(on_getOneRecordFrame(int)));
	connect(m_pAsyncRecExping, SIGNAL(finished()), this, SLOT(on_expRelatedAsyncEnd()));
	connect(status_dlg, SIGNAL(evt_cancel()), m_pAsyncRecExping, SLOT(cancel()));
	// futhre 与进度条 关联
	connect(m_pAsyncRecExping, SIGNAL(progressRangeChanged(int, int)), status_dlg->getProBar(), SLOT(setRange(int, int)));
	connect(m_pAsyncRecExping, SIGNAL(progressValueChanged(int)), status_dlg->getProBar(), SLOT(setValue(int)));
	//this->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);

	// 设置黑色背景
	QPalette pa;
	pa.setBrush(QPalette::Background, QBrush(Qt::black));
	setAutoFillBackground(true);
	setPalette(pa);
}

void QDisPlayWidget::setContexMenu()
{
	_contextMenu = new QMenu(this);
	QStringList sList = m_strRightMenuItems.split(",", QString::SkipEmptyParts);

	sList.append("导出附加参数设置");
	int nIndex = E_RIGHTMENU_SNAP;
	foreach(QString str, sList)
	{
		if (nIndex == E_RIGHTMEUN_SCALE)
		{
			QMenu* menu = new QMenu(str);
			menu->addAction("1:1", _parent, &BaseDisParentWnd::zoom11);
			menu->addAction("Zoom In", _parent, &BaseDisParentWnd::zoomIn);
			menu->addAction("Zoom Out",_parent, &BaseDisParentWnd::zoomOut);
			menu->addAction("Restore",_parent, &BaseDisParentWnd::restore);
			_contextMenu->addMenu(menu);
		}
		else
		{
			QAction* action = _contextMenu->addAction(str);
			if (nIndex == E_RIGHTMENU_LEFTRIGHT || nIndex == E_RIGHTMENU_UPDOWN)
			{
				action->setCheckable(true);
			}
		}

		++nIndex;
	}
}

QDisPlayWidget::~QDisPlayWidget()
{
	m_bRealPlay = false;
	m_bRecPlay = false;
	qDebug() << "PlayWnd:" << m_strNam << "quiting ...";
	delete m_ptJobHander;
	m_ptJobHander = NULL;
	m_Thread.quit();
	m_Thread.wait(2000);
	qDebug() << "PlayWnd:" << m_strNam << "quitted ...";
}


CURRENT_DISPLAY_TYPE QDisPlayWidget::getPlayType()
{
    if (m_bRecPlay)
    {
        return DISPLAY_TYPE_REPLAY;
    }

    if (m_bRealPlay)
    {
        return DISPLAY_TYPE_REALTIME;
    }

    

    return DISPLAY_TYPE_NONE;
}

void QDisPlayWidget::timerEvent(QTimerEvent *event)
{
	if (m_cQueenData.Queen_UsedLen() >= 1)
	{
		update();
	}
}

void QDisPlayWidget::renderStreamData(DisplayFrameData* pData, QPainter* p)
{
	unsigned short _h, _m, _s, _ms, _frame_rate;
	int _line, _column, _color_depth, _real_color_depth;
	unsigned char *_display_buf;
	unsigned char *_orig_data_buf;
	unsigned char *_read_format_buf;
	int _read_format_buf_len;
	int _decoded_buf_len;
	unsigned int _frame_index;
	unsigned int _tag;
	bool need_regen_texture = false;

	try
	{
		_line = pData->line;
		_column = pData->column;
		_color_depth = pData->color_depth;
		_real_color_depth = pData->real_color_depth;
		_h = pData->h;
		_m = pData->m;
		_s = pData->s;
		_ms = pData->ms;
		_display_buf = pData->buf;
		_orig_data_buf = pData->orig_data;
		_read_format_buf = pData->read_format_data;
		_read_format_buf_len = pData->orig_data_len;
		_decoded_buf_len = pData->decoded_buf_len;
		_frame_index = pData->frame_index;
		_tag = pData->tag;
		_frame_rate = pData->rate;
		DecodeRequestStruct request;
		request.data_source_is_resized = false;
		request.needResize = false;
		char realtime_shrink_level = 0;

		EagleControl_GetCameraRealtimeShrinkLevel(this->m_nDevID, this->m_nCCDID, &realtime_shrink_level);
		if (realtime_shrink_level != 1 && m_tOPCtx && m_tOPCtx->context_type == DISPLAY_CONTEXT_REALTIME)
		{
			request.needResize = true;
			request.data_source_is_resized = true;
			int column = _column;
			int line = _line;
			EagleData_ProcessScaledColumnLine(column, line, _color_depth, realtime_shrink_level);
			request.new_column = column;
			request.new_line = line;
		}
		request.muitiplier = 4;
		request.IsRgbSort = true;
		request.original_color_depth = _color_depth;
		request.original_column = _column;
		request.original_line = _line;
		request.original_real_color_depth = _real_color_depth;
		request.LeftRightReverse = LeftRightIsReservsed;
		request.UpDownReverse = UpDownIsReservsed;

		if ((pData->frame_type != FRAME_TYPE_DEFAULT)
			&& (m_bBitChange || is_display_mode_changed || (m_bSimuPlay && !pData->displayed)) // 暂停状态下  外部 解码变化 或者 上下颠倒了 [3/20/2017 ejitec]
			)
		{
			//调整readout format
			DecoderPluginMgr::getInstance()->getReadFormat(_format_name.toStdWString())->Decode(
				_read_format_buf,
				_read_format_buf_len,
				_orig_data_buf,
				_color_depth,
				_column,
				_line,
				&request
				);
			DecoderPlugin::DecodedParam read_format_param(_color_depth, _column, _line);
			DecoderPluginMgr::getInstance()->getReadFormat(_format_name.toStdWString())->GetDecodedParam(
				read_format_param,
				pData->read_format_color_depth,
				pData->read_format_column,
				pData->read_format_line,
				&request
				);
			int rotated = 0;
			if (pData->frame_type != FRAME_TYPE_DEFAULT)
			{
				//decode
				rotated = DecoderPluginMgr::getInstance()->getDecoder(_decoder_name.toStdWString())->Decode(
					_display_buf,
					_decoded_buf_len,
					_read_format_buf,
					pData->read_format_color_depth,
					pData->read_format_column,
					pData->read_format_line,
					&request
					);
			}

			DecoderPlugin::DecodedParam decoded_param(pData->read_format_color_depth, pData->read_format_column, pData->read_format_line);
			DecoderPluginMgr::getInstance()->getDecoder(_decoder_name.toStdWString())->GetDecodedParam(
				decoded_param,
				pData->decoded_color_depth,
				pData->decoded_column,
				pData->decoded_line,
				&request
				);
			pData->rotated = (bool)rotated;
			need_regen_texture = true;
			is_display_mode_changed = false;
			m_bBitChange = false;
		}
	}
	catch (std::runtime_error &e){
		qDebug() << e.what();
		return;
	}

	QTime time;
	time.start();

	cv::Mat mat(_line, _column, CV_8UC3, _display_buf);
	int tcv = time.elapsed();

	// 	cvNamedWindow("src", CV_WINDOW_AUTOSIZE);
	// 	cvNamedWindow("desc", CV_WINDOW_AUTOSIZE);
	// 	cvShowImage("src", cvCloneImage(&(IplImage)dst1));
	// 	cvShowImage("desc", cvCloneImage(&(IplImage)dst2));
	// 	cvWaitKey(0);
	// 	cvDestroyWindow("src");
	// 	cvDestroyWindow("desc");
	
	
	QScrollArea* transferWgt = _parent->getScrollArea()/*->viewport()*/;
	//qDebug() << transferWgt->size() << this->size() << _parent->getScrollArea()->viewport()->size() << m_scrollOffsetP;
	cv::Mat dst,dstShow;
	const char* strText;
	int tImg = 0;
	QRect drawRec = this->rect();

	if (this->size() == transferWgt->viewport()->size())  // 没有滑块
	{
		// 效率 比 Image scale 高
		cv::resize(mat, dst, cv::Size(width(), height()));
		strText = "Normal cv";
		tImg = time.elapsed();
	}
	else
	{
		float xRate = _column*1.0/this->width();
		float yRate = _line*1.0 / this->height();
		cv::Rect roi = cv::Rect(m_scrollOffsetP.x()*xRate, m_scrollOffsetP.y()*yRate,
			transferWgt->viewport()->width()*xRate, transferWgt->viewport()->height()*yRate);
		roi.width = qMin(roi.width, int(_column - m_scrollOffsetP.x()*xRate));
		roi.height = qMin(roi.height, int(_line - m_scrollOffsetP.y()*yRate));
		// 提取需要显示的区域 缩放后 再进行渲染
		cv::resize(cv::Mat(mat, roi), dst, cv::Size(transferWgt->viewport()->width(), transferWgt->viewport()->height()));
		strText = "Scroll cv";
		tImg = time.elapsed();

		drawRec = QRect(QPoint(), transferWgt->viewport()->rect().size()); //出现滑动条后 底部有2pix空白, 应为 cv Resize所致 
		drawRec.moveTopLeft(m_scrollOffsetP);
	}

	cv::flip(dst, dstShow, -1);
	// Copy input Mat  
	const uchar *pSrc = (const uchar*)dstShow.data;
	// Create QImage with same dimensions as input Mat  
	QImage image = QImage(pSrc, dstShow.cols, dstShow.rows, dstShow.step, QImage::Format_RGB888); // 变量 dst2.step 必须 
	
	p->drawPixmap(drawRec, QPixmap::fromImage(image));
	qDebug() << strText << tcv << " Img" << tImg << " draw :" << time.elapsed();
	//qDebug() << "drawRec:" <<  drawRec << " ImageRec:" << image.rect();

	if (EagleConfigFile::getInstance()->m_nShow == 1)
	{
		static Qt::GlobalColor colors[] = { Qt::red, Qt::black, Qt::white, Qt::green };
		// 画十字 [6/12/2017 xystar]
		p->setPen(QPen(colors[EagleConfigFile::getInstance()->m_nColor], EagleConfigFile::getInstance()->m_nLineWidth));
		p->translate(transferWgt->rect().center());
		p->translate(m_scrollOffsetP);

		int radisx = EagleConfigFile::getInstance()->m_tCrossSize.width() / 2;
		int radisy = EagleConfigFile::getInstance()->m_tCrossSize.height() / 2;
		p->drawLine(-1 * radisx, 0, radisx, 0);
		p->drawLine(0, -1 * radisy, 0, radisy);

		int xPos = pData->decoded_column / 2;
		int yPos = pData->decoded_line / 2;
		unsigned short hex_value = (pData->color_depth == 8) ?
			(pData->read_format_data[yPos * pData->read_format_column + xPos])
			: (((unsigned short*)pData->read_format_data)[yPos * pData->read_format_column + xPos]);
		
		QFont font = p->font();
		font.setPixelSize(12);
		p->setFont(font);
		p->drawText(0, 0, QString("P(%1,%2) V:%3").arg(xPos).arg(yPos)
			.arg(QString("").sprintf("0x%04X", hex_value)));
	}
}

void QDisPlayWidget::paintEvent(QPaintEvent *event)
{
	static QTime tm;
	tm.start();
	QPainter p(this);

#if 0

	// == 10ms [11/1/2016 ejitec]
	QStyleOption opt;
	opt.init(this);
	style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this);

#endif

	DisplayFrameData* pData = NULL;
	if (!m_cQueenData.HaveData() || !IsDisplayIng())  // 记录导出时 避免界面刷新
	{
		// == 5ms  背景贴图效率较高
		//style()->drawItemPixmap(&p, rect(), Qt::AlignCenter, QPixmap::fromImage(StaticFrameMgr::getInstance()->GetDefaultImage()));
		//qDebug() << "drawItemPixmap,escape:" << tm.elapsed();
		QString strText = "No Video";
		QRectF textRec;
		p.setPen(QPen(Qt::white));
		QFont font = p.font();
		font.setPixelSize(qMin(18,height()/16));
		p.setFont(font);
		p.drawText(this->rect(), Qt::AlignCenter, strText, &textRec);
		p.setPen(QPen(Qt::white, 1));
		int nPix = p.pen().width();
		p.drawRect(textRec.adjusted(-nPix, -nPix, nPix, nPix));
		drawLabelText();

		return;
	}

	if (m_cQueenData.Queen_UsedLen() > 1)
	{
		pData = m_cQueenData.read();  // 读取并 移动读写指针
	}
	else if (m_cQueenData.Queen_UsedLen() == 1)
	{
		pData = m_cQueenData.peek(); // 只读 不移动
		if (!m_bRecPlay && !m_bSimuPlay && !m_bRealPlay)  // 停止播放时 将剩余的队列清空 [11/1/2016 ejitec]
		{
			pData = m_cQueenData.read();
			pData->displayed = true;
			//m_cQueenData.readLastOne();
			return;
		}
	}

	tm.start();
	renderStreamData(pData, &p);
	qDebug() << "renderStreamData tm:" << tm.elapsed() << " ms";
	pData->displayed = true;
	drawLabelText(pData);

	if (m_bRecPause)
	{
		QPainter painter(this);
		int w = this->width(), h = this->height();
		// 绘制矩形 [12/9/2016 ejitec]
		if (m_bDrawRecStart)
		{
			QPen pen;       //设置画笔，颜色、宽度
			pen.setColor(Qt::green);
			pen.setWidth(1);
			pen.setStyle(Qt::DashDotDotLine);
			painter.setPen(pen);
			painter.drawRect(QRect(m_startPt, m_endPt));
		}
		else
		{
			if (m_startPt != m_endPt)
			{
				QPen pen = painter.pen();  // 默认和背景一样的画笔
				pen.setColor(Qt::green);
				pen.setStyle(Qt::SolidLine);
				painter.setPen(pen);
				painter.drawRect(QRect(m_startPt, m_endPt));
			}
		}
	}

	return QWidget::paintEvent(event);
}

void QDisPlayWidget::mousePressEvent(QMouseEvent *event)
{
	if (!m_bRecPause || !rect().contains(event->pos()))
	{
		return;
	}

	//qDebug() << "mousePressEvent:" << event->pos();
	if (event->button() == Qt::LeftButton) // 暂停下
	{
		m_bDrawRecStart = true;
		m_startPt = event->pos();
		m_endPt = event->pos();
		m_time.start();
	}
}

void QDisPlayWidget::mouseReleaseEvent(QMouseEvent *event)
{
	qDebug() << "mouseReleaseEvent:" << event->pos() << m_startPt;

	if (m_time.elapsed() < 25 && !rect().contains(event->pos()))
	{
		return;
	}

	if ((event->button() == Qt::LeftButton) && m_bDrawRecStart)
	{
		m_endPt = event->pos();
		m_endPt.setX((m_endPt.x() < 0 ? 0 : m_endPt.x()));
		m_endPt.setX((m_endPt.x() > width() ? width() - 2 : m_endPt.x()));
		m_endPt.setY((m_endPt.y() < 0 ? 0 : m_endPt.y()));
		m_endPt.setY((m_endPt.y() > height() ? height() - 2 : m_endPt.y()));
		m_bDrawRecStart = false;
		update();
	}
}

// 
void QDisPlayWidget::drawLabelText(DisplayFrameData*pData, QPoint pos)
{
    static OverlapPlugin* pfOverlap = OverlapPluginMgr::getInstance()->getDecoder(EagleConfigFile::getInstance()->export_bmp_type.toInt());

    OverlapDllReq tReq = { 0 };
    _parent->getDevNam().toWCharArray(tReq._device_name);
    _parent->getCCDNam().toWCharArray(tReq._ccd_name);
    tReq._bIsMidWnd = m_bInCenter;
    tReq._curStat = getPlayType();
    
#ifndef NORMAL_VERSION

    tReq._extraInfo = new ExtraInfoDllReq();
    memset(tReq._extraInfo, 0, sizeof(tReq._extraInfo));

#if  defined(CHENGGS_VERSION ) && CHENGGS_VERSION >= 3  // 与显示相关提前设置 [6/1/2018 xystar]
    std::strcpy((char*)tReq._extraInfo->_szConfigTxt, qPrintable(EagleConfigFile::getInstance()->m_strOverlapText));
#endif

#endif

	if (!m_bInCenter || !pData)
	{
        _parent->setLableText(QString::fromLocal8Bit(pfOverlap->GetOverlapText(&tReq).c_str()));
		
		return;
	}
	
#ifndef NORMAL_VERSION

    if (m_bInCenter && pData)
    {
        tReq._extraInfo->_width = pData->column;
        tReq._extraInfo->_height = pData->column;
        tReq._extraInfo->_colorDepth = pData->color_depth;

#ifdef CHANGGS_VERSION  // 长光

        tReq._extraInfo->_buffer = pData->read_format_data;
        tReq._extraInfo->_infoChannel = EagleConfigFile::getInstance()->GetDeviceCameraInfoChannel(DevID(), CCDID());
        tReq._extraInfo->_realColorDepth = pData->real_color_depth;

#endif

#ifdef XIANGS_VERSION

        tReq._extraInfo->_buffer = pData->frame_head;
        tReq._extraInfo->TOffset._AEOffset = std::get<1>(m_ExpParms);
        tReq._extraInfo->TOffset._timeOffset = std::get<0>(m_ExpParms) * 10;

#endif // XIANGS_VERSION

#ifdef YUAN9_VERSION

        tReq._extraInfo->_buffer = pData->read_format_data;

#endif

#ifdef CHENGGS_VERSION

        tReq._extraInfo->_buffer = pData->orig_data;
        tReq._extraInfo->_data_source = (CCDID() == 1 ? 2 : 1);

        if (CHENGGS_VERSION >= 3)  //成光 3 4 5 叠加大体一致
        {
            int fiberNum = 1;
            if (wcscmp(tReq._ccd_name, L"MTV") == 0 || wcscmp(tReq._ccd_name, L"CTV") == 0)
            {
                fiberNum = 2;
            }
            tReq._extraInfo->_data_source = fiberNum;
            std::strcpy((char*)tReq._extraInfo->_szConfigTxt, qPrintable(EagleConfigFile::getInstance()->m_strOverlapText));
        }

#endif

    }
		
#else

    tReq._hour = pData->h;
    tReq._minite = pData->m;
    tReq._second = pData->s;
    tReq._millsecond = pData->ms;

    if (m_bRecPause)
    {
        if (/*rect().contains(pos) &&*/ pos.x() > 0 && pos.y() > 0)
        {
            int xPos = pos.x() * ((float)pData->decoded_column / width());
            int yPos = pos.y() * ((float)pData->decoded_line / height());

            unsigned short hex_value = (pData->color_depth == 8) ?
                (pData->read_format_data[yPos * pData->read_format_column + xPos])
                : (((unsigned short*)pData->read_format_data)[yPos * pData->read_format_column + xPos]);
            tReq._bShowCordHex = true;
            tReq._posx = xPos;
            tReq._posy = yPos;
            tReq._hexValue = hex_value;

            // #ifdef NORMAL_VERSION
            // 				strText1 << QString("Cord:(%1,%2)").arg(xPos).arg(yPos);
            // 				strText1 << QString("V:0x%1").arg(hex_value, 4, 16, QChar('0')).toUpper();
            // #else
            // 				strText2 <<  QString("Cord:(%1,%2)").arg(xPos).arg(yPos);
            // 				strText2 <<  QString("V:0x%1").arg(hex_value, 4, 16, QChar('0')).toUpper();
            // #endif
        }
    }

#endif

    tReq._frameIndex = pData->frame_index;

	if (m_bRecPlay)
	{
		ReplayContext* ctx = static_cast<ReplayContext*>(m_tOPCtx);
		Q_ASSERT(ctx != nullptr);
		int fh = 1;
		if (ctx->fast_replay < 0)
		{
			fh = -1;
		}
		//strText1 << QString("%1X").arg(pow(2, ctx->fast_replay*fh - 1)*fh);
        tReq._replaySpeed = pow(2, ctx->fast_replay*fh - 1)*fh;
	}


    string strOverLap = pfOverlap->GetOverlapText(&tReq);
    delete tReq._extraInfo;
    tReq._extraInfo = NULL;
    _parent->setLableText(QString::fromLocal8Bit(strOverLap.c_str()));

//     if (strText2.size() == 0)
//     {
//         _parent->setLableText(strText1.join(" "));
//     }
//     else
//     {
//         _parent->setLableText(strText1.join(" ").append("\n") + strText2.join("  "));
//     }

    // TODO: drawLostFrame  [6/1/2018 xystar]

}

// 在窗体也会捕捉事件
void QDisPlayWidget::mouseMoveEvent(QMouseEvent *event)
{
	if (m_bRecPause)
	{
		DisplayFrameData*pData = m_cQueenData.peek();
		if (pData && m_bInCenter)
		{
			drawLabelText(pData, event->pos());
		}
	}

	if (m_bDrawRecStart)
	{
		if (m_time.elapsed() < 25)
		{
			return;
		}

		m_endPt = event->pos();

		update();
	}
}

QPaintEngine * QDisPlayWidget::paintEngine() const
{
	throw std::logic_error("The method or operation is not implemented.");
}

// 与其他对象连接双击事件
void QDisPlayWidget::mouseDoubleClickEvent(QMouseEvent *event)
{
	emit evt_dbClick(event);
}

// 确保当前帧 显示完成后 再卸载内存 [3/27/2017 ejitec]
void QDisPlayWidget::freeMemory()
{
	qInfo() << "--------->>" << m_strNam << " Enter freeMemory()";

	QMutexLocker locker(&_threadLock);
	qInfo() << "--------->>" << m_strNam << " freeMemory() begin";
	
	while (m_cQueenData.HaveData())
	{
		qDebug() << "m_cQueenData.Queen_UsedLen():" << m_cQueenData.Queen_UsedLen()
			<< "availableNum:" << m_cQueenData.GetAvailableNum()
			<< " m_bTheadAlive:" << m_bTheadAlive;
		m_cQueenData.Clear();
		emit m_Worker.evt_refresh();  // 刷新界面
		QThread::msleep(40);
	}
	
	emit m_Worker.evt_refresh();  // 刷新界面

	for (int i = 0; i < m_cQueenData.QueenSize(); i++)
	{
		try
		{
			if (!m_cQueenData.freeHeapData(i))
			{
				continue;
			}

			DisplayFrameData frame = *m_cQueenData.freeHeapData(i);

			free(frame.buf);
			frame.buf = NULL;
			free(frame.orig_data);
			frame.orig_data = NULL;
			free(frame.read_format_data);
			frame.read_format_data = NULL;
			free(frame.frame_head);
			frame.frame_head = NULL;
		}
		catch (std::runtime_error e)
		{
			qFatal("freeMemory erro: %s\n", e.what());
		}
	}
	m_cQueenData.Destroy();
	qInfo() << "--------->>" << m_strNam << " freeMemory() end";
}

bool QDisPlayWidget::allocMemoryForQueen(int _column, int _line, int _color_depth, int _pix_size)
{
	int capture_color_depth = _color_depth;
	if (_color_depth == 10)
		capture_color_depth = 12;
	else if (_color_depth == 14)
		capture_color_depth = 16;
	else
		capture_color_depth = _color_depth;

	int head_size = HEAD_SIZE(_column, _line, capture_color_depth);
	unsigned int frame_men_size = _line*_column * _pix_size * 2 + _line*_column * 3 + head_size;
#ifndef WIN64
	int max_frames = MAX_DISPLAY_MEN_SIZE / frame_men_size;
#else
	int max_frames = QDisPlayWidget::getIdealThreadCnt() * 2;
#endif
	max_frames = qMin(int(QDisPlayWidget::getIdealThreadCnt() * 2), max_frames);
	// qMin(max_frames, MAX_DISPLAY_QUEUE_LEN)
	m_cQueenData.InitQueueWithSize(max_frames);

	qCritical() << "w,h:" << _column << "|" << _line << " InitQueueWithSize:" << max_frames;

	for (int j = 0; j < m_cQueenData.QueenSize(); j++)
	{
		DisplayFrameData frame;
		frame.orig_data_len = _line*_column * _pix_size;
		frame.frame_head_len = head_size;
		frame.decoded_buf_len = _line*_column * 3;
		frame.buf = (unsigned char*)malloc(frame.decoded_buf_len);
		frame.orig_data = (unsigned char*)malloc(frame.orig_data_len);
		frame.read_format_data = (unsigned char*)malloc(frame.orig_data_len);
		frame.frame_head = (unsigned char*)malloc(head_size);
		frame.frame_type = FRAME_TYPE_NORMAL;
		if (frame.buf && frame.orig_data && frame.frame_head && frame.read_format_data)
		{
			m_cQueenData.pushHeapData(&frame, j);
		}
		else
		{
			if (frame.buf)
				free(frame.buf);
			if (frame.orig_data)
				free(frame.orig_data);
			if (frame.frame_head)
				free(frame.frame_head);
			if (frame.read_format_data)
				free(frame.read_format_data);
			qCritical() << "allocMemoryForQueen error at Index:" << j << ",MaxCnt:" << max_frames;
			
			return false;
		}
	}

	return true;
}


QVariant QDisPlayWidget::asyncExportCheckFrame(int frame_index)
{
	ExportContext *context = static_cast<ExportContext*>(m_tOPCtx);

	return context->check();
}

quint64 QDisPlayWidget::ExportCheckThread(ExportContext *context)
{
	unsigned long long err_pix = 0;
	qDebug() << ("Start check thread.");

	while (1)
	{
		if (context->export_finished()){
			qDebug() << ("Finish check thread.");
			break;
		}
		if (context->is_export_canceled()){
			qDebug() << ("Cancel check thread.");
			break;
		}
		err_pix += context->check();  // 检查数据  sdk 调用
		if (status_dlg->getValue() < context->get_current_export_num())   // 进度条设置
		{
			emit evt_setExpStatBar(context->get_current_export_num());
		}
	}

	HideExportStatusDlg();

	return err_pix;
}

void QDisPlayWidget::setVideoCtx()
{
	int new_color_depth = 0;  //需要重新获取 下 解码后 宽 高 
	int new_column = 0;
	int new_line = 0;
	DecodeRequestStruct request;
	request.needResize = false;
	request.IsRgbSort = false;
	request.data_source_is_resized = false;

	if (!m_tOPCtx)
	{
		return;
	}

	DecoderPlugin::DecodedParam read_format_param(m_tOPCtx->get_color_depth(), m_tOPCtx->get_column(), m_tOPCtx->get_line());
	DecoderPluginMgr::getInstance()->getReadFormat(get_read_format().toStdWString())->GetDecodedParam(
		read_format_param,
		new_color_depth,
		new_column,
		new_line,
		&request
		);
	DecoderPlugin::DecodedParam decoder_param(new_color_depth, new_column, new_line);
	DecoderPluginMgr::getInstance()->getDecoder(get_decoder().toStdWString())->GetDecodedParam(
		decoder_param,
		new_color_depth,
		new_column,
		new_line,
		&request
		);

	((ExportContext*)m_tOPCtx)->_width = new_column;
	((ExportContext*)m_tOPCtx)->_height = new_line;

	return;
}

// 接收 窗口的所有CMD命令  注意线程函数 中 引用的变量持久性
void QDisPlayWidget::customEvent(QEvent *event)
{
	if (event->type() != MyEventType)
	{
		return;
	}

	MessageCmd* cmd = static_cast<MessageCmd*>(event);  // 消息循环结束时  自动析构
	//qDebug() << "Peer msgID:" << cmd->cmd;  
	std::shared_ptr<MessageCmd> spCMD = std::make_shared<MessageCmd>(*cmd);  // 将消息保存到 共享指针 消息结束 自动销毁
	static EagleData_CcdRecord dummy_ccdrec;  // 虚拟一个录像ID  实时播放时 实际不需要
	TCbJob tJob;

	switch (cmd->cmd)
	{
	case GT_CMD_REALTIME_START_PLAY:
	{
		tJob.m_eType = E_JOBTYPE_STREAM_REAL_START;
		m_ptJobHander->postJob([spCMD, this]()  // 在线程中操作
		{
			m_tOPCtx = new RealtimeContext(*spCMD, &dummy_ccdrec, this);
			if (!m_tOPCtx)
			{
				return false;
			}
			set_decoder(spCMD->sampling_format, true);
			m_tOPCtx->valid = allocMemoryForQueen(spCMD->column, spCMD->line, spCMD->color_depth, m_tOPCtx->get_pix_size());

			if (!m_tOPCtx->valid)  // 
			{
				delete m_tOPCtx;
				m_tOPCtx = NULL;

				return false;
			}
			m_bRealPlay = true;

			return true;

		}, tJob);


		break;
	}
	case GT_CMD_REALTIME_STOP_PLAY:
	{
		_parent->toShowNormal();
		set_decoder(0, false);

		TCbJob tJob;
		tJob.m_eType = E_JOBTYPE_STREAM_REAL_STOP;
		m_ptJobHander->postJob([=]()
		{
			m_bRealPlay = false;
			if (m_tOPCtx && m_tOPCtx->valid)
			{
				freeMemory();
			}
			delete m_tOPCtx;
			m_tOPCtx = NULL;

			return true;

		}, tJob);

		break;
	}
	case GT_CMD_REPLAY_START:  // 回放开
	{
		if (m_bInCenter)
			_parent->toShowRecPlay();

		tJob.m_eType = E_JOBTYPE_STREAM_REC_START;
		if (spCMD->replay_export_method == 3)
		{
			tJob.m_eType = E_JOBTYPE_STREAM_SIMU_START;
		}

		_parent->toSetSliderRange(spCMD->Replay.replay_start_index, spCMD->Replay.replay_end_index);
		
		if (spCMD->replay_export_method == 3)
		{
			InitSimulaStatusDlg(tr("正在读取仿真数据，请稍候 ..."));
			m_bChecking = true;
		}

		m_ptJobHander->postJob([spCMD, this]()->bool  // 在线程中操作
		{
			EagleData_CcdRecord *ccdrec = EagleDataMgr::GetInstance()->GetCcdRecord
				(spCMD->task_name, spCMD->record_time, spCMD->device_id, spCMD->ccd_serial);
			
			if (spCMD->replay_export_method == 3)
			{
				EagleData_CcdRecord *ccdrec = EagleDataMgr::GetInstance()->GetCcdRecord(spCMD->task_name, spCMD->record_time, spCMD->device_id, spCMD->ccd_serial);
				// 挂盘是否到主线程进行 [11/16/2017 xystar]
				EagleData_CheckAndRemountFileSystem(spCMD->device_id, (DISK_MOUNT_TYPE)EagleConfigFile::getInstance()->disk_mount_type.toLong());
				m_tOPCtx = new SimuReplayContext(*spCMD, ccdrec, this);
				InitSimulatePlay();
				
			}
			else
			{
				m_tOPCtx = new ReplayContext(*spCMD, ccdrec, this);
			}
			
			if (!m_tOPCtx || !ccdrec)
			{
				return false;
			}
			set_decoder(ccdrec->pixel_sampling_type, true);
			m_tOPCtx->valid = allocMemoryForQueen(spCMD->column, spCMD->line, spCMD->color_depth, m_tOPCtx->get_pix_size());

			if (!m_tOPCtx->valid)  // 
			{
				return false;
			}

			if (spCMD->replay_export_method == 3)
			{
				m_bSimuPlay = true;
			}
			else
			{
				m_bRecPlay = true;
			}

			return true;

		}, tJob);

		break;
	}
	case GT_CMD_REPLAY_STOP:  // 回放关
	{
		m_bRecPlay = false;
		m_bRecPause = false;

		_parent->toShowNormal();
		set_decoder(0, false);
		tJob.m_eType = E_JOBTYPE_STREAM_REC_STOP;
		if (m_bSimuPlay )
		{
			m_bSimuPlay = false;
			if (m_tOPCtx)
				EagleData_CameralinkStopRead(m_tOPCtx->get_device_id());
			tJob.m_eType = E_JOBTYPE_STREAM_SIMU_STOP;
		}

		m_ptJobHander->postJob([=]()->bool
		{
			if (m_tOPCtx && m_tOPCtx->valid)
			{
				freeMemory();
			}

			delete m_tOPCtx;
			m_tOPCtx = NULL;

			return true;

		}, tJob);

		break;
	}
	case GT_CMD_EXPORT_START: // 导出开始
	{
		tJob.m_eType = E_JOBTYPE_STREAM_EXP_START;
		m_ptJobHander->postJob([=]()->bool
		{
			qDebug() << "GT_CMD_EXPORT_START ID:" << QThread::currentThreadId();
			ExportContext* ctx = nullptr;

			try
			{
				EagleData_CcdRecord *ccdrec = EagleDataMgr::GetInstance()->GetCcdRecord(spCMD->task_name, spCMD->record_time, spCMD->device_id, spCMD->ccd_serial);
				m_tOPCtx = new ExportContext(*spCMD, ccdrec, this);
				set_decoder(ccdrec->pixel_sampling_type, true);
				ctx = ((ExportContext*)m_tOPCtx);
			}
			catch (std::runtime_error &)
			{
				qCritical() << QString("Failed get ccd record: %1 %2 %2")
					.arg(QString::fromWCharArray(spCMD->task_name)).arg(spCMD->record_index).arg(spCMD->ccd_serial);

				return false;
			}

			if (!preperExpRecord())
			{
				return false;
			}

			m_tOPCtx->valid = allocMemoryForQueen(m_tOPCtx->get_column(), m_tOPCtx->get_line(), m_tOPCtx->get_color_depth(), m_tOPCtx->get_pix_size());
			if (!m_tOPCtx->valid)
			{
				delete m_tOPCtx;
				m_tOPCtx = NULL;

				return false;
			}

			switch (spCMD->Export.export_type)
			{
			case EExportAvi:
			{
				setVideoCtx();

				break;
			}

#ifndef ASYNC_FLIEWRITE
			case EExportRawStream1:  // win32 下多线程并发写
			{
				break;
			}
#endif
			case EExportFrameHead:
			{
				break;
			}

			default:  // 可并行的导出 模式 如 bmp  jpg tif 等

				int nCurrent = ctx->current_frame_index;

				for (auto it : ctx->time_to_index)
				{
					if ((it.first - nCurrent) % ctx->export_select_interval == 0)
					{
						ctx->m_vExpIndexs.push_back(it.second);
					}
				}

				break;
			}

			((ExportContext*)m_tOPCtx)->export_frame_index.clear();

			return true;

		}, tJob);

		break;
	}
	case GT_CMD_EXPORT_STOP:  // 
	{
		tJob.m_eType = E_JOBTYPE_STREAM_EXP_END;
		set_decoder(0, false);

		if (m_bExporting == false)
		{
			return;
		}

		m_ptJobHander->postJob([=]()->bool
		{

			if (m_pAsyncRecExping->isRunning())
			{
				m_pAsyncRecExping->cancel();
				m_pAsyncRecExping->waitForFinished();
			}

			qDebug() << "customEvent message GT_CMD_EXPORT_STOP recv ...";

			try
			{
				int total_export_num = ((ExportContext*)m_tOPCtx)->total_export_num;
				// 相机2 导出结束，导出帧数: 119
				QString strMsg = GetLogFormat(EagleLog_Export_Log_Finish2).arg(/*m_tOPCtx->get_ccd_serial()*/_parent->getCCDNam()).arg(total_export_num);
				ControlApiLogger(strMsg);
				WriteExportLog(m_tOPCtx, strMsg);
				if (EagleConfigFile::getInstance()->export_log.toInt() >= 1)
				{
					CheckExportFrameIndex(*(ExportContext*)m_tOPCtx);
				}

#ifdef ASYNC_FLIEWRITE
				ExportContext*ctx = ((ExportContext*)m_tOPCtx);
				if (ctx->export_type == EExportRawStream1 && ctx->raw_stream1_fileHd)
				{
					CloseHandle(ctx->raw_stream1_fileHd);
					ctx->raw_stream1_fileHd = NULL;
				}
#endif

				if (m_tOPCtx && m_tOPCtx->valid)
				{
					freeMemory();
				}
				delete m_tOPCtx;
				m_tOPCtx = NULL;
			}
			catch (...)
			{

			}

			return true;

		}, tJob);

		break;
	}
	case GT_CMD_EXPORT_CANCLE:  // 导出取消
	case GT_CMD_CHECK_STOP:   // 停止检查
	{
		ExportContext* ctx = (ExportContext*)m_tOPCtx;
		Q_ASSERT(ctx != NULL);
		ctx->cancel_export();

		if (m_bExporting)
		{
			emit status_dlg->evt_cancel();
		}

		if (m_bChecking)
		{
			endFutrueWatcher();
			m_bChecking = false;
			qDebug() << (tr("相机%1 检查了 %2 帧图像").arg(ctx->get_ccd_serial()).arg(ctx->total_export_num));
			unsigned long long total_pix = ctx->total_export_num* (unsigned long long)ctx->get_line() * ctx->get_column();
			ControlApiLogger(GetLogFormat(EagleLog_Check_Data_Result).arg(/*ctx->get_ccd_serial()*/_parent->getCCDNam())
				.arg(ctx->total_export_num).arg(total_pix).arg(ctx->m_dwErrPixs).arg((float)ctx->m_dwErrPixs / total_pix));
			HideExportStatusDlg();
		}

		break;
	}

	case GT_CMD_CHECK_DATA:
	{
		if (IsDisplayIng() && m_tOPCtx != nullptr)
			break;

		try
		{
			EagleData_CcdRecord *ccdrec = EagleDataMgr::GetInstance()->GetCcdRecord(spCMD->task_name, spCMD->record_time, spCMD->device_id, spCMD->ccd_serial);
			m_tOPCtx = new ExportContext(*spCMD, ccdrec, this);
		}
		catch (std::runtime_error &)
		{
			qCritical() << "Failed get ccd record:"
				<< QString::fromWCharArray(spCMD->task_name) << " "
				<< spCMD->record_index << " " << spCMD->ccd_serial;
			break;
		}
		InitExportStatusDlg(g_strListDlgTiltle[EAGLE_PROGRESS_DLG_TITLE_CHECK]);

		ExportContext* ctx = (ExportContext*)m_tOPCtx;

		for (auto it : ctx->time_to_index) 
		{
			ctx->m_vExpIndexs.push_back(it.second);
		}

		prepStartFutureWatcher();  // mapped 会独占所有 线程池线程  其余任务启动  需要更改  线程池数量
		// 开启异步 导出操作
		m_pAsyncRecExping->setFuture(QtConcurrent::mapped(ctx->m_vExpIndexs,
			std::bind(&QDisPlayWidget::asyncExportCheckFrame, this, std::placeholders::_1)));
		//ControlApiLogger("check thread started");
		m_bChecking = true;

		break;
	}
	case GT_CMD_SEND_CCD_DETECT_INFO:
	{
		if (!m_bRealPlay)
		{
			break;
		}

		detect_column = spCMD->column;
		detect_line = spCMD->line;
		detect_rate = spCMD->frame_rate;
		lose_frame = spCMD->lose_frame;
		break;
	}
	case GT_CMD_EXIT:
	{
		if (m_tOPCtx)
			m_tOPCtx->valid = false;
		m_bRecPlay = false;
		m_bRealPlay = false;

		break;
	}

	default:
		break;
	}
}

// 必须在主线程中执行的  在此进行
void QDisPlayWidget::on_ThreadFinish(TCbJob tJob)
{
	qDebug() << "on_ThreadFinish:" << QThread::currentThread();

	switch (tJob.m_eType)
	{
	case  E_JOBTYPE_STREAM_REAL_START:
	{
		qDebug() << "E_JOBTYPE_STREAM_REAL_START here!!";
		if (m_tOPCtx->valid && tJob.m_bRetOK)
		{
			emit evt_realReq();  // 开始
		}

		break;
	}
	case  E_JOBTYPE_STREAM_REAL_STOP:
	{
		qDebug() << "E_JOBTYPE_STREAM_REAL_STOP here!!";
		break;
	}
	case E_JOBTYPE_STREAM_SIMU_START:
	{
		qDebug() << "E_JOBTYPE_STREAM_SIMU_START here!!";
		QString strText;
		if (tJob.m_bRetOK)
		{
			strText = tr("读取仿真数据成功.");
		}
		else
		{
			strText = tr("读取仿真数据失败!!!");
		}
		simu_dlg->setWaitText(strText);
		m_bChecking = false;
		QTimer::singleShot(500, [this]()
		{
			simu_dlg->accept();
			if (m_tOPCtx->valid)
				emit evt_simuReq();  // 开始
		});
		

		break;
	}
	case E_JOBTYPE_STREAM_REC_START:
	{
		qDebug() << "E_JOBTYPE_STREAM_REC_START here!!";
		if (m_tOPCtx->valid && tJob.m_bRetOK)
		{
			emit evt_recReq();  // 开始
		}
		break;
	}
	case E_JOBTYPE_STREAM_REC_STOP:
	{
		qDebug() << "E_JOBTYPE_STREAM_REC_STOP here!!";

		break;
	}
	case E_JOBTYPE_STREAM_SIMU_STOP:
	{
		qDebug() << "E_JOBTYPE_STREAM_SIMU_STOP here!!";

		break;
	}
	case E_JOBTYPE_STREAM_EXP_START:  // 资源申请完毕
	{
		qDebug() << "E_JOBTYPE_STREAM_EXP_START here!!";
		m_bExporting = true;
		if (tJob.m_bRetOK && m_tOPCtx->valid)
		{
			WriteExportLog(m_tOPCtx, GetLogFormat(EagleLog_Export_Log_Start).arg(_parent->getCCDNam()).arg(m_nCCDID));  // 2016 - 11 - 10 11:52 : 53  相机2 开始导出
			// 三种对话框标题
			InitExportStatusDlg(g_strListDlgTiltle.at(EAGLE_PROGRESS_DLG_TITLE_EXPORT));

			ExportContext* ctx = static_cast<ExportContext*> (m_tOPCtx);
			if (EExportAvi == ctx->export_type
#ifndef _WIN32
				|| EExportRawStream1 == ctx->export_type
#endif
				)
			{
				emit evt_recExp();   // 执行导出任务
			}
			else
			{

#ifdef ASYNC_FLIEWRITE
				if (ctx->export_type == EExportRawStream1)
				{
					QString file_name = ctx->get_export_path() + QString::fromStdWString(ctx->get_task_name()) + export_type_str[EExportRawStream1];
					
					ctx->raw_stream1_fileHd = CreateFileA(qPrintable(file_name),
						GENERIC_WRITE,
						FILE_SHARE_READ,
						NULL,
						CREATE_ALWAYS,
						FILE_FLAG_WRITE_THROUGH/* |FILE_ATTRIBUTE_NORMAL | FILE_FLAG_NO_BUFFERING*/,
						NULL);
				}
#endif

				prepStartFutureWatcher();
				// 开启异步 导出操作
				m_pAsyncRecExping->setFuture(QtConcurrent::mapped(ctx->m_vExpIndexs,
					std::bind(&QDisPlayWidget::asyncGetOneRecordFrame, this, std::placeholders::_1)));
			}
		}
		break;
	}
	case E_JOBTYPE_STREAM_EXP_END:
	{
		m_bExporting = false;

		qDebug() << "E_JOBTYPE_STREAM_EXP_END here!!";
		endFutrueWatcher();
		HideExportStatusDlg();

		break;
	}

	default:
		break;
	}
}

// mapped 会独占所有 线程池线程  其余任务启动  需要更改  线程池数量
void QDisPlayWidget::prepStartFutureWatcher()
{
	if (QThreadPool::globalInstance()->activeThreadCount() >= QThreadPool::globalInstance()->maxThreadCount())
	{
		QThreadPool::globalInstance()->setMaxThreadCount(QThreadPool::globalInstance()->activeThreadCount() + 1);
		m_bHaveReserveThread = true;
		qInfo() << "--> Start QThreadPool::globalInstance() maxThreadCnt:" << QThreadPool::globalInstance()->maxThreadCount()
			<< " IdealCnt:" << QThread::idealThreadCount() << " activeCnt:" << QThreadPool::globalInstance()->activeThreadCount();
	}
}

void QDisPlayWidget::endFutrueWatcher()
{
	if (m_bHaveReserveThread)
	{
		QThreadPool::globalInstance()->setMaxThreadCount(/*QThread::idealThreadCount()*/getIdealThreadCnt());
		m_bHaveReserveThread = false;
		qInfo() << "--> End QThreadPool::globalInstance() maxThreadCnt:" << QThreadPool::globalInstance()->maxThreadCount()
			<< " IdealCnt:" << QThread::idealThreadCount() << " activeCnt:" << QThreadPool::globalInstance()->activeThreadCount();
	}
}

//////////////////////////////////////////////////////////////////////////
//  
bool QDisPlayWidget::preperExpRecord()
{
	ExportContext* ctx = ((ExportContext*)m_tOPCtx);

	QString strPath = QDir::fromNativeSeparators(ctx->export_path);

	if (!strPath.endsWith(QDir::separator()))
	{
		strPath.push_back(QDir::separator());
	}
	// F:\frameHead\chk-20161009 150018\A0QP215B16001-MTV-bmp-20161107171524
	strPath += QString("%1-%2-").arg(_parent->getDevNam(), _parent->getCCDNam());
	strPath += export_type_str[ctx->export_type];
	QString timeStr = QDateTime::currentDateTime().toString("-yyyyMMdd-hhmmss");
	strPath += timeStr;
	strPath.push_back(QDir::separator());

	qDebug() << "RecPath:" << strPath << " sep:" << QDir::separator();
	QDir d;
	if (!d.mkpath(strPath))
	{
		delete m_tOPCtx;
		m_tOPCtx = NULL;

		return false;
	}

	ctx->export_path = strPath;

	QString config_file_path = strPath + QString("config.txt");
	QFile file(config_file_path);
	file.open(QFile::Append);
	QTextStream data(&file);
	//write config file
	data << QString("TaskName = %1\r\n").arg(QString::fromWCharArray(m_tOPCtx->get_task_name().c_str()));
	data << QString("RecordIndex = %1\r\n").arg(m_tOPCtx->get_related_ccd_rec()->record_index);
	data << QString("Column = %1\r\n").arg(m_tOPCtx->get_column());
	data << QString("Line = %1\r\n").arg(m_tOPCtx->get_line());
	data << QString("ColorDepth = %1\r\n").arg(m_tOPCtx->get_color_depth());
	data << QString("StartTime = %1\r\n").arg(QString::fromWCharArray(m_tOPCtx->get_related_ccd_rec()->start_time));
	data << QString("EndTime = %1\r\n").arg(QString::fromWCharArray(m_tOPCtx->get_related_ccd_rec()->end_time));
	data.flush();
	file.close();

#ifdef CHENGGS_VERSION
	QString info_file_path = strPath + QString("info.txt");
	char info_buf[512];
	int	buf_size = sizeof(info_buf);
	EagleData_ReadInfoTxtFile(m_tOPCtx->get_related_ccd_rec()->record_id, m_tOPCtx->get_related_ccd_rec()->id, info_buf, &buf_size);
	std::ofstream info_ofs;
	info_ofs.open(qPrintable(info_file_path));
	info_ofs.write(info_buf, buf_size);
	info_ofs.close();
#endif

	return true;
}


#ifdef WITH_FFMPEG
//////////////////////////////////////////////////////////////////////////
//  videoStream 。。。。 [3/23/2017 ejitec]
AVFrame * QDisPlayWidget::alloc_picture(enum AVPixelFormat pix_fmt, int width, int height)
{
	AVFrame *picture;
	int ret;

	picture = av_frame_alloc();
	if (!picture)
		return NULL;

	picture->format = pix_fmt;
	picture->width = width;
	picture->height = height;

	/* allocate the buffers for the frame data */
	ret = av_frame_get_buffer(picture, 32);
	if (ret < 0)
	{
		qDebug() << ("Could not allocate frame data.");
		return NULL;
	}

	return picture;
}

bool QDisPlayWidget::open_video(AVFormatContext *oc, AVCodec *codec, OutputStream *ost, AVDictionary *opt_arg)
{
	int ret;
	AVCodecContext *c = ost->enc;
	AVDictionary *opt = NULL;

	av_dict_copy(&opt, opt_arg, 0);

	/* open the codec */
	ret = avcodec_open2(c, codec, &opt);
	av_dict_free(&opt);
	if (ret < 0)
	{
		qWarning() << "Could not open video codec:" << ret;

		return false;
	}

	/* allocate and init a re-usable frame */
	ost->frame = alloc_picture(c->pix_fmt, c->width, c->height);
	if (!ost->frame)
	{
		qWarning() << "Could not allocate video frame\n";
		return false;
	}
	ost->tmp_frame = NULL;

	ost->tmp_frame = alloc_picture(AV_PIX_FMT_RGB24, c->width, c->height);
	if (!ost->tmp_frame)
	{
		qWarning() << "Could not allocate temporary picture\n";
		return false;
	}

	/* copy the stream parameters to the muxer */
	ret = avcodec_parameters_from_context(ost->st->codecpar, c);
	if (ret < 0)
	{
		qCritical() << "Could not copy the stream parameters";
		exit(1);
	}
	return true;
}

bool QDisPlayWidget::add_stream(OutputStream *ost, AVFormatContext *oc, AVCodec **codec, enum AVCodecID codec_id, ExportContext* context)
{
	AVCodecContext *c;
	AVRational time_base = { 1, 25 };

	/* find the encoder */
	*codec = avcodec_find_encoder(codec_id);
	if (!(*codec))
	{
		fprintf(stderr, "Could not find encoder for '%s'\n",
			avcodec_get_name(codec_id));
		return false;
	}

	ost->st = avformat_new_stream(oc, NULL);
	if (!ost->st)
	{
		fprintf(stderr, "Could not allocate stream\n");
		return false;
	}
	ost->st->id = oc->nb_streams - 1;
	c = avcodec_alloc_context3(*codec);
	if (!c){
		fprintf(stderr, "Could not alloc an encoding context\n");
		return false;
	}
	ost->enc = c;

	switch ((*codec)->type) {
	case AVMEDIA_TYPE_VIDEO:
		c->codec_id = codec_id;
		c->bit_rate = 4000000;
		/* Resolution must be a multiple of two. */
		c->width = context->_width;
		c->height = context->_height;
		/* timebase: This is the fundamental unit of time (in seconds) in terms
		* of which frame timestamps are represented. For fixed-fps content,
		* timebase should be 1/framerate and timestamp increments should be
		* identical to 1. */
		ost->st->time_base = time_base;
		c->time_base = ost->st->time_base;

		c->gop_size = 15; /* emit one intra frame every twelve frames at most */
		c->pix_fmt = AV_PIX_FMT_YUV420P;
		if (c->codec_id == AV_CODEC_ID_MPEG2VIDEO) {
			/* just for testing, we also add B frames */
			c->max_b_frames = 2;
		}
		if (c->codec_id == AV_CODEC_ID_MPEG1VIDEO) {
			/* Needed to avoid using macroblocks in which some coeffs overflow.
			* This does not happen with normal video, it just happens here as
			* the motion of the chroma plane does not match the luma plane. */
			c->mb_decision = 2;
		}
		break;

	default:
		break;
	}

	/* Some formats want stream headers to be separate. */
	if (oc->oformat->flags & AVFMT_GLOBALHEADER)
		c->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;

	return true;
}
#endif

// 录像导出 同步 针对需要保持导出顺序的  avi 和 rawstream
int QDisPlayWidget::on_DoRecExportSync()
{
	qDebug() << "on_DoRecExportSync::QThreadID:" << QThread::currentThreadId();
	ExportContext* context = static_cast<ExportContext*> (m_tOPCtx);
	int	 success = -1;
	int write_num = 0;
	DisplayFrameData *pObj = nullptr;
	m_bTheadAlive = true;
	bool need_decode_frame = false;

	forever
	{
		write_num = 0;
		pObj = QueenData().GetDataAt(0);  // 只取出不移动

		if (context->export_finished())
		{
			qDebug() << ("on_DoRecExportSync Finish export ...");
			break;
		}

		if (context->is_export_canceled())
		{
			qDebug() << ("on_DoRecExportSync Cancel export ...");
			break;
		}

		if (context->export_type == EExportAvi)
		{

#ifdef WITH_FFMPEG
			need_decode_frame = true;

			if (context->output_format_context == NULL)
			{
				AVCodec* video_codec;
				QString avi_name = context->get_export_path() + QString::fromStdWString(context->get_task_name()) + ".avi";
				qDebug() << "avi_name" << avi_name;
				int ret = 0;
				ret = avformat_alloc_output_context2(&context->output_format_context, NULL, NULL, qPrintable(avi_name));
				if (!context->output_format_context)
				{
					break;
				}

				if (add_stream(&context->video_output_stream, context->output_format_context, &video_codec, context->output_format_context->oformat->video_codec, context))
				{
					success = !open_video(context->output_format_context, video_codec, &context->video_output_stream, NULL);
					/* open the output file, if needed */
					if (!(context->output_format_context->oformat->flags & AVFMT_NOFILE)) {
						int ret = avio_open(&context->output_format_context->pb, qPrintable(avi_name), AVIO_FLAG_WRITE);
						if (ret < 0)
						{
							qDebug() << ("Could not open avi file ...");
						}
					}

					/* Write the stream header, if any. */
					AVDictionary *opt = NULL;
					int ret = avformat_write_header(context->output_format_context, &opt);
					if (ret < 0)
					{
						qDebug() << "Error occurred when opening output file";
					}
				}
			}
#endif
		}
		else if (context->export_type == EExportRawStream1)
		{
			if (!context->raw_stream1_stream.is_open())
			{
				QString file_name = context->get_export_path() + QString::fromStdWString(context->get_task_name())
					+ export_type_str[EExportRawStream1];
				context->raw_stream1_stream.open(qPrintable(file_name), std::ofstream::binary | std::ofstream::out);
			}
		}

		DisplayFrameData& frame = *pObj;
		//
		long real_frame_index = context->time_to_index.at(context->current_frame_index.fetch_add(context->export_select_interval));
		frame.frame_index = real_frame_index;

		//qDebug() << "export " << frame.frame_index << ", " << context->export_end_index;

		//读数据
		DecodeRequestStruct request;
		request.needResize = false;
		request.muitiplier = 4;
		request.data_source_is_resized = false;
		request.IsRgbSort = /*context->export_type == EExportAvi ? false : */true;
		request.original_color_depth = context->get_color_depth();
		request.original_column = context->get_column();
		request.original_line = context->get_line();
		request.original_real_color_depth = context->get_related_ccd_rec()->real_color_depth;
		request.LeftRightReverse = GetLeftRightIsReversed();
		request.UpDownReverse = GetUpDownIsReversed();
		int success = EagleLibReadOneStoreFrameAndDecode(context->get_related_ccd_rec()->record_id, context->get_related_ccd_rec()->id, real_frame_index, &frame, get_read_format().toStdWString(), need_decode_frame, get_decoder().toStdWString(), &request);

		if (success == 0)
		{
			FrameHead head;
			EagleData_AnalyzeFrameHead((char*)frame.frame_head, &head);
			long head_index = ((unsigned int)head.frame_index_high) << 16 | head.frame_index_low;
			context->export_frame_index_lock.lock();
			context->export_frame_index.push_back(head_index);
			context->export_frame_index_lock.unlock();
			if (EagleConfigFile::getInstance()->export_log.toInt() >= 2)
			{
				WriteExportLog(m_tOPCtx, QString("Count index = %1, Head index = %2").arg(frame.frame_index).arg(head_index));
			}
			if (EagleConfigFile::getInstance()->export_head.toInt() == 1)
			{
				QString headname = context->export_path + QString("").sprintf("head_%ld", frame.frame_index);
				SaveRawFile((char *)frame.frame_head, frame.frame_head_len, headname);
			}
			if (context->export_type == EExportRawStream1)
			{
				//				context->raw_stream1_stream.write((char*)frame.frame_head, frame.frame_head_len);
				context->raw_stream1_stream.write((char*)frame.read_format_data, frame.read_format_column * frame.read_format_line * ((frame.read_format_color_depth + 7) / 8));
				write_num = 1;
			}
			else if (context->export_type == EExportAvi)
			{

#ifdef WITH_FFMPEG
				int ret;
				AVCodecContext *c;
				AVFrame *av_frame;
				AVFrame* tmp_frame;
				int got_packet = 0;
				AVPacket pkt = { 0 };

				c = context->video_output_stream.enc;
				av_frame = context->video_output_stream.frame;
				tmp_frame = context->video_output_stream.tmp_frame;
				ret = av_frame_make_writable(tmp_frame);
				uint8_t* dest_buf = tmp_frame->data[0];

				// 此处 可以在 avi上叠加数据 [12/21/2016 ejitec]
#if   (defined(CHENGGS_VERSION) && CHENGGS_VERSION == 3)

				QImage image;  // bim
				drawAviExtentText(frame, image);
				if (image.isNull())
				{
					cout << "drawAviExtentText error!!\n";
					continue;
				}
				int w = image.width(), h = image.height();
				for (int i = 0; i < h; i++)
				{
					memcpy(dest_buf + i * tmp_frame->linesize[0], image.constBits() + w * i * 3, w * 3);
				}

#else
				for (int i = 0; i < frame.decoded_line; i++)
				{
					memcpy(dest_buf + i * tmp_frame->linesize[0], frame.buf + frame.decoded_column * i * 3, frame.decoded_column * 3);
				}
#endif

				//				memcpy(tmp_frame->data[0], frame.buf, frame.decoded_column * frame.decoded_line * 3);
				av_init_packet(&pkt);

				if (!context->video_output_stream.sws_ctx)
				{
					context->video_output_stream.sws_ctx = sws_getContext(c->width, c->height,
						AV_PIX_FMT_RGB24,
						c->width, c->height,
						c->pix_fmt,
						SWS_BICUBIC, NULL, NULL, NULL);
					if (!context->video_output_stream.sws_ctx)
					{
						qDebug() << ("Could not initialize the conversion context\n");
					}
				}
				sws_scale(context->video_output_stream.sws_ctx,
					(const uint8_t * const *)tmp_frame->data, tmp_frame->linesize,
					0, c->height, av_frame->data, av_frame->linesize);

				/* encode the image */
				ret = avcodec_encode_video2(c, &pkt, av_frame, &got_packet);
				if (ret < 0)
				{
					qCritical() << "Error encoding video frame at frame:" << context->current_frame_index;

					exit(1);
				}

				if (got_packet)
				{
					av_packet_rescale_ts(&pkt, c->time_base, context->video_output_stream.st->time_base);
					pkt.stream_index = context->video_output_stream.st->index;

					/* Write the compressed frame to the media file. */
					//	log_packet(fmt_ctx, pkt);
					write_num = 1;
					// 写入 一帧
					av_interleaved_write_frame(context->output_format_context, &pkt);
				}
#endif
			}

			context->total_export_num.fetch_add(write_num);
			if (status_dlg->getValue() < context->get_current_export_num())
			{
				emit evt_setExpStatBar(context->get_current_export_num());
			}
		}
	}

		if (context->export_type == EExportRawStream1)
		{
			context->raw_stream1_stream.close();
		}

#ifdef WITH_FFMPEG
		else if (context->export_type == EExportAvi)
		{
			if (context->output_format_context)
			{
				av_write_trailer(context->output_format_context);
				/* Close each codec. */
				avcodec_free_context(&context->video_output_stream.enc);
				av_frame_free(&context->video_output_stream.frame);
				av_frame_free(&context->video_output_stream.tmp_frame);
				sws_freeContext(context->video_output_stream.sws_ctx);

				if (!(context->output_format_context->oformat->flags & AVFMT_NOFILE))
					/* Close the output file. */
					avio_closep(&context->output_format_context->pb);

				/* free the stream */
				avformat_free_context(context->output_format_context);
			}
		}
#endif

	m_bTheadAlive = false;
	qDebug() << "RecExp_Sync type:" << context->export_type << " threadLoop quit ....";
	//
	qApp->postEvent(this, new MessageCmd(GT_CMD_EXPORT_STOP));

	return 0;
}


void QDisPlayWidget::drawAviExtentText(DisplayFrameData& data, QImage& image)
{
	DFMCODE A = { 0 }, E = { 0 };
	int w = data.decoded_column, h = data.decoded_line;
	image = QImage(data.buf, w, h, QImage::Format_RGB888);
	QPainter dc;
	dc.begin(&image);

	gtBmpExtension ext = { 0 };
    static OverlapPlugin* pfOverlap = OverlapPluginMgr::getInstance()->getDecoder(EagleConfigFile::getInstance()->export_bmp_type.toInt());
	
    int fiberNum = 1;
    if (_parent->getCCDNam().compare("MTV") == 0 || _parent->getCCDNam().compare("CTV") == 0)
    {
        fiberNum = 2;
    }

    ExtraInfoDllReq _extraInfo = { 0 };
    _extraInfo._data_source = fiberNum;
    _extraInfo._buffer = data.orig_data;
    _extraInfo._width = data.column;
    _extraInfo._height = data.line;
    _extraInfo._colorDepth = data.color_depth;
    pfOverlap->SetExtraInfo(&_extraInfo, &ext);

    OverlapPlugin::Angle_Double_To_DFM(ext.azimuth_angle / 1000000.0, &A);
    double pitch = ext.pitch_angle / 1000000.0 - 5;
    OverlapPlugin::Angle_Double_To_DFM(pitch < 0 ? -pitch : pitch, &E);  // 度分秒 中只需要 度 显示负数
    if (pitch < 0)
    {
        E.du *= -1;
    }

	/************************************************************************/
	/*
	IOE  GD220-2 XXX(设备编号)  MTV(或CTV或MIR)  XXXX-XX-XX(年月日)  XX:XX:XX(时分秒)
	*/
	/************************************************************************/
	unsigned short devNo = 0;
	unsigned long nDate = 0;

	memcpy(&nDate, ext.reserved, 4);
	memcpy(&devNo, ext.reserved + 4, 2);

	int nYear = nDate / 10000;
	nDate -= nYear * 10000;
	int nM = nDate / 100;
	int nD = nDate % 100;

	unsigned short _h, _m, _s, _ms;

    OverlapPlugin::CalcAbsoluteTime(ext.current_time, &_h, &_m, &_s, &_ms);
    _ms /= 10;

	// 设置字体的映射关系
	int nFontSize = 20;
	if (m_nCCDID == 3)
	{
		nFontSize = 18;
	}
	else if (m_nCCDID == 2)
	{
		nFontSize = 48;
	}
	else if (m_nCCDID == 1)
	{
		nFontSize = 40;
		if (w == 512)
		{
			nFontSize = 18;
		}
	}

	QFont font = dc.font();
	font.setFamily("Mono");  // Mono    Consolas
	font.setPixelSize(nFontSize);
	font.setWeight(QFont::Medium);
	dc.setFont(font);
	dc.setPen(QPen(QColor(0, 128, 0)));
	int size = dc.fontMetrics().averageCharWidth();  //dc.GetCharWidth();
	QPoint posText;
	posText.setX(2 * size); //偏移2个汉字
	QString strText;
	QTextStream text_ost1(&strText);

	strText.clear();
	text_ost1 << QString("IOE %3 %1 %2 ").arg(devNo, 3, 10, QChar('0')).arg(_parent->getCCDNam())
        .arg(EagleConfigFile::getInstance()->m_strOverlapText);
	QDateTime dt = QDateTime(QDate(nYear, nM, nD), QTime(_h, _m, _s, _ms));
	if (!dt.isValid())
	{
		text_ost1 << QString("").sprintf("%04d-%02d-%02d %02d:%02d:%02d %03d", nYear, nM, nD, _h, _m, _s, _ms);
	}
	else
	{
		text_ost1 << dt.toString("yyyy-MM-dd hh:mm::ss zzz");
	}

	posText.setY(dc.fontMetrics().height() + 2);
	dc.drawText(posText, strText);

	strText.clear();
	text_ost1 << QString("").sprintf("A: %03d°%02d′%02d.%03d″", A.du, A.fen, A.miao, A.millmiao);
	text_ost1 << QString("").sprintf("    E: %03d°%02d′%02d.%03d″", E.du, E.fen, E.miao, E.millmiao);

	posText += QPoint(0, dc.fontMetrics().height() + 2);
	dc.drawText(posText, strText);

	unsigned long fraps = ext.fraps;
	unsigned long focal = ext.focal_len;

	strText.clear();
	text_ost1 << "F:" << focal / 100 << "mm";
	text_ost1 << "    Fps:" << (fraps / 100);
	text_ost1 << "    N:" << data.frame_index;
	posText += QPoint(0, dc.fontMetrics().height() + 2);
	dc.drawText(posText, strText);
}

// 使用concurrent 异步获取 视频帧
QVariant QDisPlayWidget::asyncGetOneRecordFrame(int n)
{
	ExportContext* context = static_cast<ExportContext*> (m_tOPCtx);
	DisplayFrameData *pObj = nullptr;
	bool bRet = true;
    static OverlapPlugin* pfOverlap = OverlapPluginMgr::getInstance()->getDecoder(EagleConfigFile::getInstance()->export_bmp_type.toInt());

	while (!m_pAsyncRecExping->isFinished())
	{
		if (m_pAsyncRecExping->isCanceled())
		{
			qDebug() << ("asyncGetOneRecordFrame Cancel export ...");
			break;
		}

		pObj = m_cQueenData.PreWrite_WO();
		if (!pObj)
		{
			qDebug() << "wait here,Len:" << QueenData().Queen_UsedLen() << " free:" << QueenData().GetFreeAvailableNum()
				<< " full:" << QueenData().GetAvailableNum();
			QThread::msleep(10);
			continue;
		}

		DisplayFrameData& frame = *pObj;
		// 抽帧 保存  按照指定间隔获取 当前帧索引
		frame.frame_index = n;
		//读数据
		DecodeRequestStruct request;
		request.needResize = false;
		request.muitiplier = 4;
		request.IsRgbSort = true;
		request.data_source_is_resized = false;
		request.original_color_depth = context->get_color_depth();
		request.original_column = context->get_column();
		request.original_line = context->get_line();
		request.original_real_color_depth = context->get_related_ccd_rec()->real_color_depth;
		request.LeftRightReverse = LeftRightIsReservsed;
		request.UpDownReverse = UpDownIsReservsed;
		int success = EagleLibReadOneStoreFrameAndDecode(context->get_related_ccd_rec()->record_id, context->get_related_ccd_rec()->id
			, n, &frame, get_read_format().toStdWString(), false, get_decoder().toStdWString(), &request);

		if (success != 0)
		{
			qDebug() << "EagleLibReadOneStoreFrameAndDecode error:" << success;
			m_cQueenData.EndWrite_WO(pObj);
			continue;
		}

		FrameHead head;
		EagleData_AnalyzeFrameHead((char*)frame.frame_head, &head);
		long head_index = ((unsigned int)head.frame_index_high) << 16 | head.frame_index_low;
		QString name;
		if (EagleConfigFile::getInstance()->export_name_using_time.toInt() == 1)
		{
			QDateTime dt = QDateTime::fromTime_t(context->get_related_ccd_rec()->record_id.start_time);
			name = context->export_path + dt.toString("yyyyMMddhhmmsszzz.") + export_type_str[context->export_type];
		}
		else
		{
			name = context->export_path + QString("%1.").arg(frame.frame_index, 8, 10, QChar('0')) + export_type_str[context->export_type];
		}

		context->export_frame_index_lock.lock();
		context->export_frame_index.push_back(head_index);
		context->export_frame_index_lock.unlock();

		WriteExportLog(context, QString("Count index = %1, Head index = %2").arg(frame.frame_index).arg(head_index));
		// 导出 头
		if (EagleConfigFile::getInstance()->export_head.toInt() == 1)
		{
			QString headname = context->export_path + QString("%1head_%2").arg(QDir::separator()).arg(frame.frame_index);
			SaveRawFile((char *)frame.frame_head, frame.frame_head_len, headname);
		}

		long export_bmp_type = EagleConfigFile::getInstance()->export_bmp_type.toLong();
		//////////////////////////////////////////////////////////////////////////
		// 导出对应 格式
		if (context->export_type == EExportRaw)
		{	//raw
			if (!SaveRawFile((char *)frame.read_format_data, frame.read_format_column * frame.read_format_line * ((frame.read_format_color_depth + 7) / 8), name))
			{
				bRet = false;
			}
		}

#ifdef ASYNC_FLIEWRITE
		else if (context->export_type == EExportRawStream1)
		{
			OVERLAPPED    m_ovWriter = { 0 };
			uint32_t framLen = frame.read_format_column * frame.read_format_line * ((frame.read_format_color_depth + 7) / 8);
			DWORD wV;
			uint64_t dataLen = uint64_t(framLen)*context->m_vExpIndexs.indexOf(n);
			m_ovWriter.OffsetHigh = dataLen >> 32;
			m_ovWriter.Offset = uint32_t(dataLen) & 0xffffffff;

			BOOL bl = WriteFile(context->raw_stream1_fileHd,
				(char *)frame.read_format_data, 
				framLen,
				&wV,
				&m_ovWriter);
			if (!bl)
			{
// 				if (GetLastError() == ERROR_IO_PENDING)
// 				{
// 					bl = GetOverlappedResult(context->raw_stream1_fileHd,
// 						&m_ovWriter,
// 						&wV,
// 						TRUE);
// 
// 					cout << "writeLen:" << wV << ",Offset:" << 
// 						m_ovWriter.OffsetHigh << "," << m_ovWriter.Offset << " " << bl << endl;
// 				}

				bRet = false;
			}
		}
#endif
        else if (context->export_type == EExportBmp
            && export_bmp_type != E_VENDOR_NORMAL
            && export_bmp_type != E_VENDOR_9YUAN  // 9院的Bmp需要解码 [4/27/2018 xystar]
            && context->get_related_ccd_rec()->pixel_sampling_type == PixelSamplingGrey
#if defined(CHENGGS_VERSION)  // 3、4 期  非CTV 通道外 [12/21/2016 ejitec]
            && context->get_ccdNam() != L"CTV")
#else  // 五期
            )

#endif
        {
            int column = frame.read_format_column, line = frame.read_format_line, color_depth = frame.read_format_color_depth;
            bool left_right_reverse = GetLeftRightIsReversed();
            bool up_down_reverse = GetUpDownIsReversed();
            gtBMP bmp(column, line, color_depth, frame.read_format_data, frame.orig_data_len, up_down_reverse, left_right_reverse);
            ExtraInfoDllReq _extraInfo = { 0 };

            _extraInfo._colorDepth = frame.read_format_color_depth;
            _extraInfo._realColorDepth = frame.real_color_depth;
            _extraInfo._width = frame.read_format_column;
            _extraInfo._height = frame.read_format_line;

            if (export_bmp_type == 2)  // 长春
            {
                _extraInfo._buffer = frame.read_format_data;
                _extraInfo._infoChannel = EagleConfigFile::getInstance()->GetDeviceCameraInfoChannel(DevID(),CCDID());
                pfOverlap->SetExtraInfo(&_extraInfo, (gtBmpExtension*)bmp.get_extension_buffer());
            }
            else if (export_bmp_type == 1) // 1 --- 成都
            {
                int fiber_numer = 1;
                int nVer = 2;
                _extraInfo._buffer = frame.orig_data;

#ifdef CHENGGS_VERSION

                nVer = CHENGGS_VERSION;
                if (context->get_ccdNam() == L"MTV" || context->get_ccdNam() == L"CTV")
                {
                    fiber_numer = 2;
                }

#endif

                _extraInfo._data_source = fiber_numer;
                if (nVer == 3 || nVer == 4)
                {
                    // 内部对 bmp头进行再 赋值
                    pfOverlap->SetExtraInfo(&_extraInfo, (gtBmpExtension*)bmp.get_extension_buffer(), &bmp.getHeader());
                }
                else  // 4/5
                {
                    pfOverlap->SetExtraInfo(&_extraInfo, (gtBmpExtension*)bmp.get_extension_buffer());
                }
            }
            else if (export_bmp_type == 3)
            {
                _extraInfo._buffer = frame.frame_head;
                pfOverlap->SetExtraInfo(&_extraInfo, (gtBmpExtension*)bmp.get_extension_buffer());
            }
            bmp.writeToFile(name.toStdWString());
        }
        else if (context->export_type == EExportBmpGrey)
        {
            int column = frame.read_format_column, line = frame.read_format_line, color_depth = frame.read_format_color_depth;
            bool left_right_reverse = GetLeftRightIsReversed();
            bool up_down_reverse = GetUpDownIsReversed();
            gtBMP bmp(column, line, color_depth, frame.read_format_data, frame.orig_data_len, up_down_reverse, left_right_reverse);
            bmp.writeToFile(name.toStdWString());
        }
		else
		{
			request.IsRgbSort = true;
			int rotate = DecoderPluginMgr::getInstance()->getDecoder(get_decoder().toStdWString())->Decode(
				frame.buf,
				frame.decoded_buf_len,
				frame.read_format_data,
				frame.read_format_color_depth,
				frame.read_format_column,
				frame.read_format_line,
				&request
				);
			DecoderPlugin::DecodedParam decoded_param(frame.read_format_color_depth, frame.read_format_column, frame.read_format_line);
			DecoderPluginMgr::getInstance()->getDecoder(get_decoder().toStdWString())->GetDecodedParam(
				decoded_param,
				frame.decoded_color_depth,
				frame.decoded_column,
				frame.decoded_line,
				&request
				);

            // 需要进行解码后 导出bmp 的 通道 
            if (export_bmp_type != E_VENDOR_NORMAL
                && export_bmp_type != E_VENDOR_9YUAN
                && context->export_type == EExportBmp
                && (context->get_related_ccd_rec()->pixel_sampling_type < PixelSamplingMax
#if defined(CHENGGS_VERSION)
                || context->get_ccdNam() == L"CTV"))
#else  // 五期
                ))
#endif
            {
                int column = frame.read_format_column, line = frame.read_format_line, color_depth = frame.read_format_color_depth;
                // RGB 通道使用 解码
                gtBMP bmp(frame.decoded_column, frame.decoded_line, frame.buf, frame.decoded_buf_len);
                ExtraInfoDllReq _extraInfo = { 0 };
                _extraInfo._colorDepth = frame.read_format_color_depth;
                _extraInfo._realColorDepth = frame.real_color_depth;
                _extraInfo._width = frame.read_format_column;
                _extraInfo._height = frame.read_format_line;
                if (export_bmp_type == E_VENDOR_CHANGGS)  // 长光
                {

                    _extraInfo._buffer = frame.read_format_data;
                    _extraInfo._infoChannel = EagleConfigFile::getInstance()->GetDeviceCameraInfoChannel(DevID(),CCDID());
                    pfOverlap->SetExtraInfo(&_extraInfo, (gtBmpExtension*)bmp.get_extension_buffer());
                }
                else if (export_bmp_type == E_VENDOR_CHENGS)  // 成光 
                {
                    _extraInfo._buffer = frame.orig_data;
                    _extraInfo._data_source = 2;
#if defined(CHENGGS_VERSION)
                    // CTV 都是 2路光纤
                    if (CHENGGS_VERSION == 3 || CHENGGS_VERSION == 4)
                    {
                        // 内部对 bmp头进行再 赋值
                        pfOverlap->SetExtraInfo(&_extraInfo, (gtBmpExtension*)bmp.get_extension_buffer(), &bmp.getHeader());
                    }
                    else  // 
                    {
                        // 成光 CTV 通道 都是双路光纤
                        pfOverlap->SetExtraInfo(&_extraInfo, (gtBmpExtension*)bmp.get_extension_buffer());
                    }
#endif
                }
                else if (export_bmp_type == E_VENDOR_XIANGS)  // 西光
                {
                    _extraInfo._buffer = frame.frame_head;
                    pfOverlap->SetExtraInfo(&_extraInfo, (gtBmpExtension*)bmp.get_extension_buffer());
                }

                bmp.writeToFile(name.toStdWString());
            }
			else
			{
				int rotate_column = frame.decoded_column;
				int rotate_line = frame.decoded_line;
				if (rotate)
				{
					std::swap(rotate_column, rotate_line);
				}

				cv::Mat mat(rotate_line, rotate_column, CV_8UC3, frame.buf);
				cv::Mat dst;
				/*flip code: 0 vertical, 1 horizontal, -1 vertical & horizontal*/
				cv::flip(mat, dst, -1);
				QImage Img = QImage(dst.data, rotate_column, rotate_line, QImage::Format_RGB888);
				if (!SaveImageFile(Img, context->export_type, name))
				{
					bRet = false;
				}

				/************************************************************************/
				/*
				使用 QImage 构造图片有点问题
				*/
				/************************************************************************/
				// 				QImage Img = QImage(frame.buf, rotate_column, rotate_line, QImage::Format_RGB888);
				// 				Img.mirrored(true, true);
				// 				if (!SaveImageFile(Img, context->export_type, name))
				// 				{
				// 					bRet = false;
				// 				}
			}
		}
		// 释放数据 
		m_cQueenData.EndWrite_WO(pObj);

		break;
	}
	
	qDebug() << "asyncGet Index:" << n << " " << QThread::currentThreadId() << " useLen:" << m_cQueenData.Queen_UsedLen()
		<< " free:" << m_cQueenData.GetFreeAvailableNum()
		<< " full:" << m_cQueenData.GetAvailableNum() << " Pos:" << m_cQueenData.getWriteIndex();

	return bRet;
}

void QDisPlayWidget::on_expRelatedAsyncEnd()
{
	if (m_bExporting)
	{
		qApp->postEvent(this, new MessageCmd(GT_CMD_EXPORT_STOP));
		qInfo() << "------->> on_expAsyncEnd got msg GT_CMD_EXPORT_STOP send ....";
	}
	else
	{
		qApp->postEvent(this, new MessageCmd(GT_CMD_CHECK_STOP));
		qInfo() << "------->> on_expAsyncEnd got msg GT_CMD_CHECK_STOP send ....";
	}
}

void QDisPlayWidget::on_getOneRecordFrame(int n)
{
	static_cast<ExportContext*>(m_tOPCtx)->total_export_num.fetch_add(1);

	if (m_bChecking)  // 自检时
	{
		//qDebug() << " Future finished index:" << n << " ret:" << m_pAsyncRecExping->resultAt(n);
		static_cast<ExportContext*>(m_tOPCtx)->m_dwErrPixs.fetch_add(m_pAsyncRecExping->resultAt(n).toInt());
	}
}

// 录像导出  异步  多线程同时进行
int QDisPlayWidget::on_DoRecExportAsync(int nIndex)
{
	qDebug() << "on_DoRecExportAsync::QThreadID:" << QThread::currentThreadId() << " Index:" << nIndex;

#if 0

	ExportContext* context = static_cast<ExportContext*> (m_tOPCtx);
	int write_num = 0;
	DisplayFrameData *pObj = nullptr;
	m_bTheadAlive = true;

	while (true)
	{
		write_num = 0;
		if (context->export_finished())
		{
			qDebug() << ("on_DoRecExportAsync Finish export ...");
			break;
		}

		if (context->is_export_canceled())
		{
			qDebug() << ("on_DoRecExportAsync Cancel export ...");
			break;
		}

		pObj = QueenData().GetDataAt(nIndex);  // 只取出不移动
		if (!pObj)
		{
			qDebug() << "wait here,Len:" << QueenData().Queen_UsedLen();
			QThread::msleep(40);
			continue;
		}

		DisplayFrameData& frame = *pObj;

		if (context->current_frame_index >= context->export_end_index || context->total_export_num >= context->get_export_frame_num())
		{
			qDebug() << "CurrentIndex:" << context->current_frame_index << ",endIndex:" << context->export_end_index
				<< ",Totle:" << context->total_export_num << ",ExportNum:" << context->get_export_frame_num();
		}

		// 抽帧 保存  按照指定间隔获取 当前帧索引
		long real_frame_index = context->time_to_index.at(context->current_frame_index.fetch_add(context->export_select_interval));
		frame.frame_index = real_frame_index;
		//qDebug() << QString("%3:export %1, %2").arg(frame.frame_index).arg(context->export_end_index).arg(m_strNam);

		//读数据
		DecodeRequestStruct request;
		request.needResize = false;
		request.muitiplier = 4;
		request.IsRgbSort = true;
		request.data_source_is_resized = false;
		request.original_color_depth = context->get_color_depth();
		request.original_column = context->get_column();
		request.original_line = context->get_line();
		request.original_real_color_depth = context->get_related_ccd_rec()->real_color_depth;
		request.LeftRightReverse = LeftRightIsReservsed;
		request.UpDownReverse = UpDownIsReservsed;

#ifdef XIANGS_VERSION
		memcpy(request.Reserved, &std::get<0>(m_ExpParms), 4);
		memcpy(request.Reserved + 4, &std::get<1>(m_ExpParms), 4);
		memcpy(request.Reserved + 8, &std::get<2>(m_ExpParms), 4);
#endif
		int success = 0;
		success = EagleLibReadOneStoreFrameAndDecode(context->get_related_ccd_rec()->record_id, context->get_related_ccd_rec()->id
			, real_frame_index, &frame, get_read_format().toStdWString(), false, get_decoder().toStdWString(), &request);

		if (success != 0)
		{
			qDebug() << "EagleLibReadOneStoreFrameAndDecode error:" << success;
			continue;
		}
		FrameHead head;
		EagleData_AnalyzeFrameHead((char*)frame.frame_head, &head);
		long head_index = ((unsigned int)head.frame_index_high) << 16 | head.frame_index_low;
		QString name;
		if (EagleConfigFile::getInstance()->export_name_using_time.toInt() == 1)
		{
			QDateTime dt = QDateTime::fromTime_t(context->get_related_ccd_rec()->record_id.start_time);
			name = context->export_path + dt.toString("yyyyMMddhhmmsszzz.") + export_type_str[context->export_type];
		}
		else
		{
			name = context->export_path + QString("%1.").arg(frame.frame_index, 8, 10, QChar('0')) + export_type_str[context->export_type];
		}

		context->export_frame_index_lock.lock();
		context->export_frame_index.push_back(head_index);
		context->export_frame_index_lock.unlock();

		WriteExportLog(context, QString("Count index = %1, Head index = %2").arg(frame.frame_index).arg(head_index));
		// 导出 头
		if (EagleConfigFile::getInstance()->export_head.toInt() == 1)
		{
			QString headname = context->export_path + QString("\\head_%d").arg(frame.frame_index);
			SaveRawFile((char *)frame.frame_head, frame.frame_head_len, headname);
		}

		long export_bmp_type = EagleConfigFile::getInstance()->export_bmp_type.toLong();
		//////////////////////////////////////////////////////////////////////////
		// 导出对应 格式
		if (context->export_type == EExportRaw)
		{//raw
			if (SaveRawFile((char *)frame.read_format_data, frame.read_format_column * frame.read_format_line * ((frame.read_format_color_depth + 7) / 8), name)) 
			{
				write_num = 1;
			}
		}
		else if (context->export_type == EExportBmp && export_bmp_type != 0
			&& context->get_related_ccd_rec()->pixel_sampling_type == PixelSamplingGrey
			&& context->get_ccd_serial() != 3)// 导出自定义BMP格式数据  原始数据已经解码
		{
			int column = frame.read_format_column, line = frame.read_format_line, color_depth = frame.read_format_color_depth;
			bool left_right_reverse = GetLeftRightIsReversed();
			bool up_down_reverse = GetUpDownIsReversed();
			gtBMP bmp(column, line, color_depth, frame.read_format_data, frame.orig_data_len, up_down_reverse, left_right_reverse);
			if (export_bmp_type == 2)  // 长春
			{
				int info_channel = EagleConfigFile::getInstance()->GetDeviceCameraInfoChannel(
					m_nDevID, context->get_ccd_serial());
				CHANG_GS_DATA info_data;
				BaseBmpExtensionInfo::GetChannelData(frame.read_format_data + ((line - 1) * column * ((color_depth + 7) / 8)), info_channel, frame.real_color_depth, &info_data);
				setExtraInfo((gtBmpExtension*)bmp.get_extension_buffer(), (unsigned char*)&info_data, 30, export_bmp_type);
			}
			else  // 1 --- 成都
			{
				int fiber_numer = 1;
				int column = frame.read_format_column;
				int line = frame.read_format_line;
				int color_depth = frame.read_format_color_depth;
#ifdef CHENGGS_VERSION
				if (context->get_ccd_serial() == 1)
					fiber_numer = 2;
#endif
				setExtraInfo((gtBmpExtension*)bmp.get_extension_buffer(), column, line, color_depth, fiber_numer, frame.orig_data, frame.orig_data_len, export_bmp_type);
			}
			bmp.writeToFile(name.toStdWString());
			write_num = 1;
		}
		else if (context->export_type == EExportBmpGrey)
		{
			int column = frame.read_format_column, line = frame.read_format_line, color_depth = frame.read_format_color_depth;
			bool left_right_reverse = GetLeftRightIsReversed();
			bool up_down_reverse = GetUpDownIsReversed();
			gtBMP bmp(column, line, color_depth, frame.read_format_data, frame.orig_data_len, up_down_reverse, left_right_reverse);
			bmp.writeToFile(name.toStdWString());
			write_num = 1;

		}
		else
		{
			request.IsRgbSort = true;
			int rotate = DecoderPluginMgr::getInstance()->getDecoder(get_decoder().toStdWString())->Decode(
				frame.buf,
				frame.decoded_buf_len,
				frame.read_format_data,
				frame.read_format_color_depth,
				frame.read_format_column,
				frame.read_format_line,
				&request
				);
			DecoderPlugin::DecodedParam decoded_param(frame.read_format_color_depth, frame.read_format_column, frame.read_format_line);
			DecoderPluginMgr::getInstance()->getDecoder(get_decoder().toStdWString())->GetDecodedParam(
				decoded_param,
				frame.decoded_color_depth,
				frame.decoded_column,
				frame.decoded_line,
				&request
				);

			if (export_bmp_type != 0 &&   // 3通道需要进行解码运算
				(context->get_related_ccd_rec()->pixel_sampling_type < PixelSamplingMax || context->get_ccd_serial() == 3))
			{
				int column = frame.read_format_column, line = frame.read_format_line, color_depth = frame.read_format_color_depth;
				gtBMP bmp(frame.decoded_column, frame.decoded_line, frame.buf, frame.decoded_buf_len);
				if (export_bmp_type == 2)
				{
					int info_channel = EagleConfigFile::getInstance()->GetDeviceCameraInfoChannel(m_nDevID, context->get_ccd_serial());
					CHANG_GS_DATA info_data;
					BaseBmpExtensionInfo::GetChannelData(frame.read_format_data + ((line - 1) * column * ((color_depth + 7) / 8)), info_channel, frame.real_color_depth, &info_data);
					setExtraInfo((gtBmpExtension*)bmp.get_extension_buffer(), (unsigned char*)&info_data, 30, export_bmp_type);
				}
				else if (export_bmp_type == 1)
				{
					setExtraInfo((gtBmpExtension*)bmp.get_extension_buffer(), frame.read_format_column, frame.read_format_line, frame.read_format_color_depth,
						context->get_ccd_serial() == 1 ? 2 : 1, frame.orig_data, frame.orig_data_len, export_bmp_type);
				}
				bmp.writeToFile(name.toStdWString());
				write_num = 1;
			}
			else
			{
				int rotate_column = frame.decoded_column;
				int rotate_line = frame.decoded_line;
				if (rotate)
				{
					std::swap(rotate_column, rotate_line);
				}

				QImage Img = QImage(frame.buf, rotate_column, rotate_line, QImage::Format_RGB888);
				Img.mirrored(true, true);
				if (SaveImageFile(Img, context->export_type, name))
				{
					write_num = 1;
				}
			}
		}

		context->total_export_num.fetch_add(write_num);

		if (status_dlg->getValue() <= context->total_export_num)
		{
			emit evt_setExpStatBar(context->total_export_num);
		}
	}

	context->liveThreadCnt.fetch_add(-1);
	qDebug() << "RecExp_Async type:" << context->export_type
		<< " ThreadID:" << QThread::currentThreadId() << " quitting ...";

	if (context->liveThreadCnt.load() == 0)
	{
		m_bTheadAlive = false;
		qApp->postEvent(this, new MessageCmd(GT_CMD_EXPORT_STOP));
		qDebug() << "------->> Last thead quit msg GT_CMD_EXPORT_STOP send ....";
	}

#endif

	return 0;
}

// 实时请求 [3/20/2017 ejitec]
int QDisPlayWidget::on_DoStreamRealReq()
{
	static QTime tm;
	RealtimeContext* context = static_cast<RealtimeContext*>(m_tOPCtx);
	DisplayFrameData *pobj = nullptr;
	quint32 success = 0;
	QMutexLocker locker(&_threadLock);

	while (m_bRealPlay)
	{
		tm.start();
		pobj = QueenData().PreWrite();
		if (!pobj)
		{
			qDebug() << context->get_device_id() << "," << context->get_ccd_serial()
				<< ": wait here,Len:" << QueenData().Queen_UsedLen() << ",free:" << m_cQueenData.GetFreeAvailableNum()
				<< ",full:" << m_cQueenData.GetAvailableNum();
			emit m_Worker.evt_refresh();  // 刷新界面
			std::this_thread::sleep_for(std::chrono::milliseconds(10));
			continue;
		}

		DisplayFrameData& display_frame = *pobj;
		display_frame.real_color_depth = context->get_color_depth();
		display_frame.color_depth = context->get_color_depth();

		if (context->get_color_depth() == 10) {
			display_frame.color_depth = 12;
		}
		else if (context->get_color_depth() == 14) {
			display_frame.color_depth = 16;
		}

		EagleData_CcdRecord_Id id;
		id.device_id = context->get_device_id();
		id.ccd_serial = context->get_ccd_serial();
		success = EagleData_ReadOneRealtimeFrame(id, context->get_column(), context->get_line(), context->get_color_depth(), (char*)display_frame.orig_data, display_frame.orig_data_len, (char*)display_frame.frame_head, display_frame.frame_head_len);

		if (success == 0)
		{
			context->current_frame_index++;
			FrameHead head;
			EagleData_AnalyzeFrameHead((char*)display_frame.frame_head, &head);
			display_frame.displayed = false;
			display_frame.rotated = false;
			display_frame.line = context->get_line();//head.frame_line;
			display_frame.column = context->get_column();//head.frame_column;

			display_frame.h = head.h;
			display_frame.m = head.m;
			display_frame.s = head.s;
			display_frame.ms = head.ms;
			//frame.frame_index = ((unsigned int)head.frame_index_high) << 16 | head.frame_index_low;
			display_frame.frame_index = context->current_frame_index;
			display_frame.tag = head.tag;
			DecodeRequestStruct request;
			char realtime_shrink_level = 0;

			EagleControl_GetCameraRealtimeShrinkLevel(id.device_id, id.ccd_serial, &realtime_shrink_level);

			request.needResize = true;
			request.muitiplier = 4;
			request.IsRgbSort = true;
			request.data_source_is_resized = realtime_shrink_level != 1;
			request.original_color_depth = display_frame.color_depth;
			request.original_column = context->get_column();
			request.original_line = context->get_line();
			request.original_real_color_depth = display_frame.real_color_depth;
			request.LeftRightReverse = GetLeftRightIsReversed();
			request.UpDownReverse = GetUpDownIsReversed();
			if (request.data_source_is_resized)
			{
				int column = display_frame.column;
				int line = display_frame.line;
				EagleData_ProcessScaledColumnLine(column, line, display_frame.color_depth, realtime_shrink_level);
				request.new_column = column;
				request.new_line = line;
			}

			//调整readout format
			DecoderPluginMgr::getInstance()->getReadFormat(get_read_format().toStdWString())->Decode(
				display_frame.read_format_data,
				display_frame.orig_data_len,
				display_frame.orig_data,
				display_frame.color_depth,
				display_frame.column,
				display_frame.line,
				&request
				);

			// 原始解码参数 [11/23/2016 ejitec]
			DecoderPlugin::DecodedParam read_format_param(display_frame.color_depth, display_frame.column, display_frame.line);
			DecoderPluginMgr::getInstance()->getReadFormat(get_read_format().toStdWString())->GetDecodedParam(
				read_format_param,
				display_frame.read_format_color_depth,
				display_frame.read_format_column,
				display_frame.read_format_line,
				&request
				);
			//decode
			int rotated = DecoderPluginMgr::getInstance()->getDecoder(get_decoder().toStdWString())->Decode(
				display_frame.buf,
				display_frame.decoded_buf_len,
				display_frame.read_format_data,
				display_frame.read_format_color_depth,
				display_frame.read_format_column,
				display_frame.read_format_line,
				&request
				);
			DecoderPlugin::DecodedParam decoded_param(display_frame.read_format_color_depth, display_frame.read_format_column, display_frame.read_format_line);
			DecoderPluginMgr::getInstance()->getDecoder(get_decoder().toStdWString())->GetDecodedParam(
				decoded_param,
				display_frame.decoded_color_depth,
				display_frame.decoded_column,
				display_frame.decoded_line,
				&request
				);
			display_frame.rotated = (bool)rotated;
		}
		if (success == 0)
		{
			QueenData().EndWrite();
			emit m_Worker.evt_refresh();  // 刷新界面
		}
		else
		{
			qDebug() << m_strNam << " RealTime EagleData_ReadOneRealtimeFrame,Ret:" << success;
			QueenData().EndWrite(false);
		}

		if (tm.elapsed() - 35 < 0)  // 读取频率略大于 25 帧
		{
			QThread::msleep(40 - tm.elapsed());
		}
	}

	return 0;
}
// 录像请求 thread
int QDisPlayWidget::on_DoStreamRecReq()
{
	static QTime tm;
	//std::cout << "QTime:" << tm.elapsed() << endl;
	ReplayContext* context = static_cast<ReplayContext*>(m_tOPCtx);
	m_bTheadAlive = true;
	// 内存数据循环覆盖
	DisplayFrameData *pobj = nullptr;
	bool stoped = false;
	int success = 0;
	QMutexLocker locker(&_threadLock);

	while (m_bRecPlay && !stoped)
	{
		tm.start();
		if (m_bRecPause && !context->m_bSinglePlay)
		{
			QThread::msleep(10);
			continue;
		}

		pobj = QueenData().PreWrite();
		if (!pobj)
		{
			qDebug() << context->get_device_id() << "," << context->get_ccd_serial()
				<< ": wait here,Len:" << QueenData().Queen_UsedLen() << ",free:" << m_cQueenData.GetFreeAvailableNum()
				<< ",full:" << m_cQueenData.GetAvailableNum();
			emit m_Worker.evt_refresh();  // 刷新界面
			QThread::msleep(40);
			continue;
		}
		DisplayFrameData& display_frame = *pobj;
		display_frame.displayed = false;
		long read_real_index = context->time_to_index.at(context->current_frame_index);
		DecodeRequestStruct request;
		// CURRENT_DISPLAY_MODE_EXPAND == ((DisplayPanel*)frame_buffer->main_panel->GetParent())->GetCurrentDisplayMode() ? true : false;;
		request.needResize = false;
		request.muitiplier = 4;
		request.IsRgbSort = true;
		request.data_source_is_resized = false;
		request.original_color_depth = context->get_color_depth();
		request.original_column = context->get_column();
		request.original_line = context->get_line();
		request.original_real_color_depth = context->get_related_ccd_rec()->real_color_depth;
		request.LeftRightReverse = LeftRightIsReservsed;
		request.UpDownReverse = UpDownIsReservsed;
		//tm.start();
		success = EagleLibReadOneStoreFrameAndDecode(context->get_related_ccd_rec()->record_id
			, context->get_related_ccd_rec()->id, read_real_index
			, &display_frame, get_read_format().toStdWString(), true
			, get_decoder().toStdWString(), &request);
		//std::cout << "EagleLibReadOneStoreFrameAndDecode QTime:" << tm.elapsed() << endl;
		if (success == 0)
		{
			context->current_frame_index += context->replay_select_interval * (pow(2.0, abs(context->fast_replay) - 1)
				*(context->fast_replay / abs(context->fast_replay)));
			if (context->current_frame_index > context->replay_end_index)
			{
				context->current_frame_index = context->replay_end_index;
				qDebug() << "if evt_recReq called " << m_nDevID << ":" << m_nCCDID;
				stoped = true;
			}

			if (context->current_frame_index < context->replay_start_index)
				context->current_frame_index = context->replay_start_index;

			display_frame.frame_index = read_real_index;
		}
		else  // 不需要刷新
		{
			if (context->replay_end_index == context->current_frame_index)
			{
				qDebug() << "else evt_recReq called " << m_nDevID << ":" << m_nCCDID;
				stoped = true;
			}
			QThread::msleep(10);
		}

		if (m_bRecPause && context->m_bSinglePlay)
		{
			context->lock.lock();
			context->m_bSinglePlay = false;
			context->lock.unlock();
		}

		if (success == 0)
		{
			emit evt_setSlider(context->current_frame_index);
			QueenData().EndWrite();
			emit m_Worker.evt_refresh();  // 刷新界面
		}
		else  // 出错
		{
			QueenData().EndWrite(false);  // 不 移动 写指针
		}

		if (stoped)  // 自动结束
		{
			emit evt_stopRec();
			emit m_Worker.evt_refresh();  // 刷新界面
		}

		if (tm.elapsed() - 35 < 0)  // 读取频率略大于 25 帧
		{
			QThread::msleep(40 - tm.elapsed());
		}
	}

	qDebug() << context->get_device_id() << "," << context->get_ccd_serial() << ": Recplay threadLoop quit ....";
	m_bTheadAlive = false;

	return 0;
}


int QDisPlayWidget::on_DoSimuRealReq()
{
	static QTime tm;
	SimuReplayContext* context = static_cast<SimuReplayContext*>(m_tOPCtx);
	m_bTheadAlive = true;
	// 内存数据循环覆盖
	DisplayFrameData *pobj = nullptr;
	bool stoped = false;
	int success = 0;

	QMutexLocker locker(&_threadLock);

	while (m_bSimuPlay && !stoped)
	{
		tm.start();
		if (m_bRecPause && !context->m_bSinglePlay)
		{
			QThread::msleep(10);
			continue;
		}

		pobj = QueenData().PreWrite();
		if (!pobj)
		{
			qDebug() << context->get_device_id() << "," << context->get_ccd_serial()
				<< ": wait here,Len:" << QueenData().Queen_UsedLen() << ",free:" << m_cQueenData.GetFreeAvailableNum()
				<< ",full:" << m_cQueenData.GetAvailableNum();
			emit m_Worker.evt_refresh();  // 刷新界面
			QThread::msleep(40);
			continue;
		}
		DisplayFrameData& frame = *pobj;

		if (m_tOPCtx->get_color_depth() == 10) {
			frame.real_color_depth = 10;
			frame.color_depth = 12;
		}
		else if (m_tOPCtx->get_color_depth() == 14) {
			frame.real_color_depth = 14;
			frame.color_depth = 16;
		}
		else {
			frame.real_color_depth = m_tOPCtx->get_color_depth();
			frame.color_depth = m_tOPCtx->get_color_depth();
		}

		EagleData_CcdRecord_Id id;
		id.device_id = m_tOPCtx->get_device_id();
		id.ccd_serial = m_tOPCtx->get_ccd_serial();
		success = EagleData_ReadOneRealtimeFrame(id, m_tOPCtx->get_column(), m_tOPCtx->get_line(), m_tOPCtx->get_color_depth(), 
		(char*)frame.orig_data, frame.orig_data_len, (char*)frame.frame_head, frame.frame_head_len);
		if (success != 0)
		{
			qDebug() << m_strNam << " Simula EagleData_ReadOneRealtimeFrame,Ret:" << success;
			if (context->replay_end_index <= context->current_frame_index)
			{
				qDebug() << "else evt_recReq called " << m_nDevID << ":" << m_nCCDID;
				stoped = true;
			}
			QThread::msleep(10);
			QueenData().EndWrite(false);  // 不 移动 写指针
		}
		else
		{
			FrameHead head;
			EagleData_AnalyzeFrameHead((char*)frame.frame_head, &head);
			frame.displayed = false;
			frame.rotated = false;
			frame.line = m_tOPCtx->get_line();//head.frame_line;
			frame.column = m_tOPCtx->get_column();//head.frame_column;
			frame.h = head.h;
			frame.m = head.m;
			frame.s = head.s;
			frame.ms = head.ms;
			unsigned long current_head_index = ((unsigned int)head.frame_index_high) << 16 | head.frame_index_low;
			frame.frame_index = current_head_index - context->start_index_head_index;
			frame.tag = head.tag;
			eagle_cmlink_export_status status;
			int nRet = EagleData_CameralinkGetExportStatus(context->get_device_id(), &status);

			if (m_bRecPause && context->m_bSinglePlay)
			{
				context->m_bSinglePlay = false;

				if (context->m_bSingleMode) // 单帧进
				{
					context->lock.lock();
					context->current_frame_index += 1;
					context->lock.unlock();
				}
				else
				{
					context->lock.lock();
					context->current_frame_index -= 1;
					context->lock.unlock();
				}
			}
			else
			{
				if (nRet == 0)
				{
					context->current_frame_index = context->current_start_index + status.current_export_number;
				}
			}

			qDebug() << context->get_device_id() << "," << context->get_ccd_serial()
				<< " EagleData_CameralinkGetExportStatus,nRet:" << nRet
				<< " Current_export_number:"
				<< status.current_export_number << endl;

			emit evt_setSlider(context->current_frame_index);
			QueenData().EndWrite();
			emit m_Worker.evt_refresh();  // 刷新界面
		}

		if (stoped)  // 自动结束
		{
			emit evt_stopRec();
			emit m_Worker.evt_refresh();  // 刷新界面
		}

		if (tm.elapsed() - 35 < 0)  // 读取频率略大于 25 帧
		{
			QThread::msleep(40 - tm.elapsed());
		}
	}

	qDebug() << context->get_device_id() << "," << context->get_ccd_serial() << ": SimulatePlay threadLoop quit ....";
	m_bTheadAlive = false;

	return 0;
}

/*
// resize的时候 效率比较低 [11/1/2016 ejitec]
void QDisPlayWidget::resizeEvent(QResizeEvent *event) 
{
// 	qDebug() << "resizeEvent:" << event->oldSize() << event->size();
// 	QWidget::resizeEvent(event);

	return;
	// 	QPalette pal(palette());
	// 	pal.setBrush(QPalette::Window, 
	// 		QBrush(StaticFrameMgr::getInstance()->GetDefaultImage().scaled(event->size(),Qt::IgnoreAspectRatio,Qt::SmoothTransformation)));
}
*/

void QDisPlayWidget::on_btnPlay()
{
	m_bRecPause = !m_bRecPause;

	if (m_bRecPlay)
	{
		if (m_bRecPause)
		{
			if (!hasMouseTracking())
			{
				setMouseTracking(true);
			}
		}
		else
		{
			setMouseTracking(false);
			m_startPt = m_endPt = QPoint(0, 0);
		}
	}
	else if (m_bSimuPlay)
	{
		if (m_bRecPause)
		{
			eagle_cmlink_export_status export_status;
			int ret = EagleData_CameralinkGetExportStatus(m_tOPCtx->get_device_id(), &export_status);
			if (ret == 0)
			{
				SimuReplayContext *rep_context = (SimuReplayContext*)m_tOPCtx;
				rep_context->current_frame_index = rep_context->current_start_index + export_status.current_export_number;
			}
			EagleData_CameralinkStopRead(m_tOPCtx->get_device_id());
		}
		else
		{
			EagleData_CameralinkStopRead(m_tOPCtx->get_device_id());
			SimuReplayContext *rep_context = (SimuReplayContext*)m_tOPCtx;
			// 恢复上下播放按钮的 使能 [10/9/2016 ejitec]
			eagle_cmlink_export_info export_info;
			SetCmlink_export_info(rep_context->current_frame_index, rep_context->replay_end_index - rep_context->current_frame_index + 1, &export_info);
			
			int ret = EagleData_CameralinkStartRead(m_tOPCtx->get_device_id(), &export_info);
			if (ret != 0)
			{
				qDebug() << "CameralinkStartRead rtn:" << ret;
			}
			rep_context->current_start_index = rep_context->current_frame_index;
		}
	}
	

	emit evt_setPlayStatus(m_bRecPause);
}

void QDisPlayWidget::on_btnFaster()
{
	if (!m_bRecPlay)
	{
		return;
	}

	ReplayContext *rep_context = (ReplayContext*)m_tOPCtx;
	rep_context->fast_replay++;
	if (rep_context->fast_replay == 0)
		rep_context->fast_replay = 1;
	if (rep_context->fast_replay > 5)
		rep_context->fast_replay = 5;
	if (m_bRecPause)
	{
		m_bRecPause = false;
	}
}

void QDisPlayWidget::on_btnLower()
{
	if (!m_bRecPlay)
	{
		return;
	}

	ReplayContext *rep_context = (ReplayContext*)m_tOPCtx;
	rep_context->fast_replay--;
	if (rep_context->fast_replay == 0)
		rep_context->fast_replay = -1;
	if (rep_context->fast_replay < -5)
		rep_context->fast_replay = -5;

	if (m_bRecPause)
	{
		m_bRecPause = false;
	}
}

void QDisPlayWidget::on_btnPrev()
{
	if (!m_bRecPause)
	{
		return;
	}

	if (m_bRecPlay)
	{
		ReplayContext *rep_context = (ReplayContext*)m_tOPCtx;

		rep_context->lock.lock();
		if (rep_context->fast_replay > 0)
		{
			rep_context->current_frame_index -= 2 * rep_context->replay_select_interval * (int)pow(2.0, abs(rep_context->fast_replay) - 1);
			if (rep_context->current_frame_index > rep_context->replay_end_index)
				rep_context->current_frame_index = rep_context->replay_end_index;
			if (rep_context->current_frame_index < rep_context->replay_start_index)
				rep_context->current_frame_index = rep_context->replay_start_index;
		}
		rep_context->m_bSinglePlay = true;
		rep_context->lock.unlock();
	}
	else if (m_bSimuPlay)
	{
		SimuReplayContext *rep_context = (SimuReplayContext*)m_tOPCtx;
		if (rep_context->current_frame_index == rep_context->replay_start_index)
		{
			return;
		}

		EagleData_CameralinkStopRead(m_tOPCtx->get_device_id());
		eagle_cmlink_export_info export_info;
		SetCmlink_export_info(rep_context->current_frame_index - 2, 1, &export_info);
		int ret = EagleData_CameralinkStartRead(m_tOPCtx->get_device_id(), &export_info);
		if (ret != 0)
		{
			qDebug() << "CameralinkStartRead rtn:" << ret;
		}

		if (!rep_context->m_bSinglePlay)
		{
			rep_context->m_bSinglePlay = true;
		}
		rep_context->m_bSingleMode = false;
	}

}

void QDisPlayWidget::on_btnNext()
{
	if (!m_bRecPause)
	{
		return;
	}

	if (m_bRecPlay)
	{
		ReplayContext *rep_context = (ReplayContext*)m_tOPCtx;
		rep_context->lock.lock();
		if (rep_context->fast_replay < 0)
		{
			rep_context->current_frame_index += 2 * rep_context->replay_select_interval * (int)pow(2.0, abs(rep_context->fast_replay) - 1);
			if (rep_context->current_frame_index > rep_context->replay_end_index)
				rep_context->current_frame_index = rep_context->replay_end_index;
			if (rep_context->current_frame_index < rep_context->replay_start_index)
				rep_context->current_frame_index = rep_context->replay_start_index;
		}
		rep_context->m_bSinglePlay = true;
		rep_context->lock.unlock();
	}
	else if (m_bSimuPlay)
	{
		SimuReplayContext *rep_context = (SimuReplayContext*)m_tOPCtx;
		if (rep_context->current_frame_index == rep_context->replay_end_index)
		{
			return;
		}

		EagleData_CameralinkStopRead(m_tOPCtx->get_device_id());

		eagle_cmlink_export_info export_info;
		SetCmlink_export_info(rep_context->current_frame_index, 1, &export_info);
		int ret = EagleData_CameralinkStartRead(m_tOPCtx->get_device_id(), &export_info);
		if (ret != 0)
		{
			qDebug() << "CameralinkStartRead rtn:" << ret;
		}
		if (!rep_context->m_bSinglePlay)
		{
			rep_context->m_bSinglePlay = true;
		}
		rep_context->m_bSingleMode = true;
		cout << "[Debug] " << "single Next insert " << endl;
	}
}

void QDisPlayWidget::on_slidValueChange(int n)
{
	if (ReplayContext* ctx = dynamic_cast<ReplayContext*>(m_tOPCtx))
	{
		ctx->lock.lock();
		ctx->current_frame_index = n;
		ctx->lock.unlock();
	}

	if (m_bSimuPlay)
	{
		SimuReplayContext* ctx = dynamic_cast<SimuReplayContext*>(m_tOPCtx);
		if (!m_bRecPause)
		{
			emit evt_setSlider(ctx->current_frame_index);
			return;
		}

		if (abs(ctx->current_frame_index - n) < 5)
		{
			emit evt_setSlider(ctx->current_frame_index);  // 非暂停下  进度条复位
			return;
		}

		ctx->lock.lock();
		ctx->current_frame_index = n;
		ctx->lock.unlock();
	}
}


void QDisPlayWidget::InitSimulaStatusDlg(QString title)
{
	simu_dlg->setWaitText(title);
	// 移动到显示窗口 中央
	QRect statRec = simu_dlg->geometry();
	statRec.moveCenter(mapToGlobal(this->rect().center()));
	simu_dlg->setGeometry(statRec);
	simu_dlg->toShow();
}

void QDisPlayWidget::ShowOpRetMsg(QString title)
{
	simu_dlg->setWaitText(title);
	QTimer::singleShot(500, [this]()
	{
		simu_dlg->accept();
	});
}

//
void QDisPlayWidget::InitExportStatusDlg(QString title)
{
	status_dlg->setWindowTitle(title);
	// 移动到显示窗口 中央
	QRect statRec = status_dlg->geometry();
	statRec.moveCenter(mapToGlobal(this->rect().center()));
	status_dlg->setGeometry(statRec);
	status_dlg->SetContext(m_tOPCtx);

}

void QDisPlayWidget::HideExportStatusDlg()
{
	m_UiTask.Post([this]()
	{
		status_dlg->toClose();
	});
}

DisplayFrameData *QDisPlayWidget::getCurrentDisplayData()
{
	if (!m_bRealPlay && !m_bRecPlay && !m_bSimuPlay)
		return NULL;
	if (!QueenData().HaveData())
		return NULL;
	return QueenData().peek();
}

// 播放窗口右键功能 [12/8/2016 ejitec]
void QDisPlayWidget::contextMenuEvent(QContextMenuEvent * event)
{
	QList<QAction*> acList = _contextMenu->actions();
	// 读取上次 勾选的值
	acList.at(E_RIGHTMENU_UPDOWN)->setChecked(UpDownIsReservsed);
	acList.at(E_RIGHTMENU_LEFTRIGHT)->setChecked(LeftRightIsReservsed);
	QAction *selectedAction = _contextMenu->exec(event->globalPos());
	int nChoice = acList.indexOf(selectedAction);
	DisplayFrameData *frame_data = NULL;
	QPoint startPt;
	QPoint endPt;

	// 西光所
	if (nChoice == E_RIGHTMENU_EXTRAPARAM)
	{
		extraParamsDlg dlg(this);
		dlg.exec();

		return;
	}

	if (nChoice < E_RIGHTMENU_UPDOWN)
	{
		frame_data = this->getCurrentDisplayData();
		if (frame_data == NULL)
			return;

		startPt = QPoint(m_startPt.x() * ((float)frame_data->decoded_column / width()),
			m_startPt.y() * ((float)frame_data->decoded_line / height()));
		endPt = QPoint(m_endPt.x() * ((float)frame_data->decoded_column / width()),
			m_endPt.y() * ((float)frame_data->decoded_line / height()));
	}
	

	switch (nChoice)
	{
	case E_RIGHTMENU_SNAP:
	{
		snapDlg dlg(this, _decoder_name, startPt, endPt, frame_data->read_format_column, frame_data->read_format_line,
			frame_data->real_color_depth, frame_data->read_format_data);

		dlg.setWindowTitle(QString("%1_%2 - %3").arg(_parent->getDevNam()).arg(_parent->getCCDNam()).arg(dlg.windowTitle()));
		dlg.exec();

		break;
	}
	case E_RIGHTMENU_STATISTIC:
	{
		// do plot chart
		StatisticsAnalyze dlg(this, startPt, endPt, frame_data->read_format_column, frame_data->read_format_line,
			frame_data->real_color_depth, frame_data->read_format_data);
		dlg.setWindowTitle(QString("%1_%2 - %3").arg(_parent->getDevNam()).arg(_parent->getCCDNam()).arg(dlg.windowTitle()));
		dlg.exec();

		break;
	}
	case E_RIGHTMENU_PIXEL:
	{
		hexViewDlg dlg(this, startPt, endPt, frame_data->read_format_column, frame_data->read_format_line,
			frame_data->real_color_depth, frame_data->read_format_data);

		dlg.setWindowTitle(QString("%1_%2 - %3").arg(_parent->getDevNam()).arg(_parent->getCCDNam()).arg(dlg.windowTitle()));
		dlg.exec();

		break;
	}
	case E_RIGHTMENU_OUTFORMAT:
	case E_RIGHTMENU_DECODE:
	{
		bool bChange = false;
		bitSetDlg dlg(this, m_bBitChange, frame_data->decoded_color_depth, _decoder_name, _format_name, nChoice == 4);

		if (nChoice == E_RIGHTMENU_DECODE)
		{
			dlg.setWindowTitle(QString("%1_%2 - 位宽设置").arg(_parent->getDevNam()).arg(_parent->getCCDNam()));
		}
		else
		{
			dlg.setWindowTitle(QString("%1_%2 - 输出格式设置").arg(_parent->getDevNam()).arg(_parent->getCCDNam()));
		}

		dlg.exec();  // 内部改变外部变量

		break;
	}
	case E_RIGHTMENU_UPDOWN:
	{
		//UpDownIsReservsed = selectedAction->isChecked();
		UpDownReversed();
		selectedAction->setChecked(GetUpDownIsReversed());

		break;
	}
	case E_RIGHTMENU_LEFTRIGHT:
	{
		//LeftRightIsReservsed = selectedAction->isChecked();
		LeftRightReversed();
		selectedAction->setChecked(GetLeftRightIsReversed());
		break;
	}
	case E_RIGHTMEUN_SCALE:
	{
		
		break;
	}

	default:
		break;
	}
}

void QDisPlayWidget::on_scrollBarValueChange(int n)
{
	//  [6/12/2017 xystar]
	if (sender() == _parent->getScrollArea()->horizontalScrollBar())
	{
		m_scrollOffsetP.setX(n);
	}
	else
	{
		m_scrollOffsetP.setY(n);
	}

	update();

	qDebug() << "Scroll offset(x,y):" << m_scrollOffsetP;
}

int QDisPlayWidget::InitSimulatePlay()
{
	SimuReplayContext *context = (SimuReplayContext*)m_tOPCtx;
	
#ifdef SIMULATE_HAS_IMPLEMENTED
	// 往下推送 disk 
	EagleData_RemoveFileSystem(context->get_device_id(),(DISK_MOUNT_TYPE)EagleConfigFile::getInstance()->disk_mount_type.toInt());
	
	eagle_cmlink_export_info export_info;
	SetCmlink_export_info(context->replay_start_index, context->replay_end_index - context->replay_start_index + 1, &export_info);

	int ret = EagleData_CameralinkStartRead(context->get_device_id(), &export_info);
	if (ret != 0)
	{
		qDebug() << "CameralinkStartRead rtn:" << ret;
	}
	context->current_start_index = context->replay_start_index;
	
#endif

	return 0;
}

void QDisPlayWidget::SetCmlink_export_info(int nStart, int framNum,eagle_cmlink_export_info* pinfo)
{
	SimuReplayContext *context = (SimuReplayContext*)m_tOPCtx;

	eagle_cmlink_export_info& export_info = *pinfo;
	EagleData_CcdRecord* ccd_record = m_tOPCtx->get_related_ccd_rec();
	memset(&export_info, 0x00, sizeof(export_info));
	std::wstring task_name = std::wstring(ccd_record->record_id.task_name);
	std::string task_name_asni = std::string(task_name.begin(), task_name.end());
	int len = sprintf(export_info.exp_name, "TASK%04d_RECD%04d_%s", ccd_record->task_index, ccd_record->record_index, task_name_asni.c_str());
	export_info.frame_scale = 1;
	export_info.start_frame = nStart;
	export_info.read_frame_num = framNum;
	export_info.frame_rate = context->frame_rate;
	export_info.frame_taps = context->frame_taps;
	export_info.column = ccd_record->column;
	export_info.line = ccd_record->line;
	export_info.color_depth = ccd_record->color_depth;
}

//
StreamWorker::StreamWorker(QDisPlayWidget* ptCaller)
:m_ptCaller(ptCaller)
{
	// 单独处理 
	connect(ptCaller, &QDisPlayWidget::evt_recExp, this, &StreamWorker::on_DoRecExport);
}
// 每个播放窗口对应一个 线程循环请求流数据
void StreamWorker::on_DoStreamRecReq()
{
	m_ptCaller->on_DoStreamRecReq();
}

void StreamWorker::on_DoStreamRealReq()
{
	m_ptCaller->on_DoStreamRealReq();
}

void StreamWorker::on_DoRecExport()
{
	m_ptCaller->on_DoRecExportSync();
}

void StreamWorker::on_DoSimuRealReq()
{
	m_ptCaller->on_DoSimuRealReq();
}

//////////////////////////////////////////////////////////////////////////
// 播放窗口容器 或 父类 [11/4/2016 ejitec]
BaseDisParentWnd::BaseDisParentWnd(QWidget* parent, TCCDInfo& info, QString strDev, QString strNam)
:QWidget(parent)
, _ccdInfo(info)
, _devNam(strDev)
{
	ui.setupUi(this);

	_playIco = new QIcon(":/res/image/Play.png");
	_pauseIco = new QIcon(":/res/image/Pause.png");
	ui.btn_play->setIcon(*_pauseIco);
	ui.playSlider->setTracking(true);  // 拖动时实时设置 value
	_disPlayWnd = new QDisPlayWidget(ui.scrollArea, strNam);
	_disPlayWnd->setCaller(this);

	connect(_disPlayWnd, &QDisPlayWidget::evt_setSlider, this, [this](int n)
	{
		if (!ui.playSlider->isSliderDown())    // 按下拖动时 不进行帧设置
		{
			ui.playSlider->setValue(n);
		}
	});

	connect(_disPlayWnd, &QDisPlayWidget::evt_setPlayStatus, this, [this](bool bPause)
	{
		if (bPause)
		{
			ui.btn_play->setIcon(*_playIco);
		}
		else
		{
			ui.btn_play->setIcon(*_pauseIco);
		}
	});

	connect(ui.playSlider, &QSlider::valueChanged, _disPlayWnd, &QDisPlayWidget::on_slidValueChange);
	connect(ui.btn_play, &QPushButton::clicked, _disPlayWnd, &QDisPlayWidget::on_btnPlay);
	connect(ui.btn_next, &QPushButton::clicked, _disPlayWnd, &QDisPlayWidget::on_btnNext);
	connect(ui.btn_prev, &QPushButton::clicked, _disPlayWnd, &QDisPlayWidget::on_btnPrev);
	connect(ui.btn_faster, &QPushButton::clicked, _disPlayWnd, &QDisPlayWidget::on_btnFaster);
	connect(ui.btn_lower, &QPushButton::clicked, _disPlayWnd, &QDisPlayWidget::on_btnLower);
	connect(ui.scrollArea->horizontalScrollBar(), &QScrollBar::valueChanged, _disPlayWnd, &QDisPlayWidget::on_scrollBarValueChange);
	connect(ui.scrollArea->verticalScrollBar(), &QScrollBar::valueChanged, _disPlayWnd, &QDisPlayWidget::on_scrollBarValueChange);
	_disPlayWnd->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding);
	ui.scrollArea->setWidgetResizable(false);
 	QVBoxLayout* vLay = new QVBoxLayout;
 	_disPlayWnd->setLayout(vLay);  // 放入 scroll 前必须设置布局，否则需要设置 widgetResizable 为 true
	//qDebug() << "ui.scrollArea->widgetResizable:" << ui.scrollArea->widgetResizable();
	ui.scrollArea->setWidget(_disPlayWnd);
	ui.scrollArea->installEventFilter(this);
	_disPlayWnd->installEventFilter(this);
	ui.scrollArea->horizontalScrollBar()->installEventFilter(this);
	ui.scrollArea->verticalScrollBar()->installEventFilter(this);
	ui.scrollArea->setAlignment(Qt::AlignCenter);
	ui.scrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
	ui.scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded);
	ui.scrollArea->setFocusPolicy(Qt::ClickFocus);
	m_strNam = strNam;

	toShowNormal();
	QPalette pe;
	ui.widget_info->setAutoFillBackground(true);
	
#ifdef NORMAL_VERSION 

	//ui.widget_info->setBackgroundRole(QPalette::WindowText);
	pe.setBrush(QPalette::Background, Qt::lightGray);
	ui.widget_info->setPalette(pe);
	ui.widget_info->setFixedHeight(24);
	pe.setColor(QPalette::WindowText, Qt::black);
	ui.label_playInfo->setPalette(pe);
	QFont font("Consolas", 12, QFont::Medium);
	ui.label_playInfo->setFont(font);
	ui.label_playInfo->setAlignment(Qt::AlignLeft);
	
#else

	ui.verticalLayout->removeWidget(ui.scrollArea);
	ui.verticalLayout->removeWidget(ui.widget_ctrl);
	ui.verticalLayout->removeWidget(ui.widget_info);
	ui.verticalLayout->addWidget(ui.widget_info);
	ui.verticalLayout->addWidget(ui.scrollArea);
	ui.verticalLayout->addWidget(ui.widget_ctrl);
	pe.setBrush(QPalette::Background, Qt::black);
	ui.widget_info->setPalette(pe);
	pe.setColor(QPalette::WindowText, Qt::green);
	ui.label_playInfo->setPalette(pe);
	ui.label_playInfo->setAlignment(Qt::AlignCenter);
	ui.label_playInfo->setScaledContents(true);
    //ui.label_playInfo->setTextFormat(Qt::RichText);
    //第一个属性是字体（微软雅黑）  /*"Microsoft YaHei"*/"Consolas" ，第二个是大小，第三个是加粗（权重是75） 
    QFont font("Microsoft YaHei", 11, QFont::DemiBold);
	ui.label_playInfo->setFont(font);
	ui.verticalLayout->setStretch(1,1);
	// 文字过长 是否自动换行
	ui.label_playInfo->setWordWrap(false);
	
	QTimer::singleShot(0, this,
		[this]()
		{
			ui.widget_info->setMinimumHeight(ui.label_playInfo->fontMetrics().height() * 2 + 2);	
		});
#endif
 
}

bool BaseDisParentWnd::eventFilter(QObject *watched, QEvent *event)
{
	if (watched == ui.scrollArea)
	{
		if (event->type() == QEvent::Resize)
		{
			QResizeEvent* evt = (QResizeEvent*)event;

// 			qDebug() << "Resize Scroll view B" << ui.scrollArea->viewport()->size()
// 				<< " Area:" << ui.scrollArea->size() << " NewOld:" << evt->size() << evt->oldSize()
// 				<< ui.scrollArea->maximumViewportSize();

			if (evt->oldSize() != evt->size())  // 缩放事件 有2种 viewport和scroll
			{
				// 缩放 scroll 自适应
				ui.scrollArea->viewport()->resize(ui.scrollArea->maximumViewportSize());
				_disPlayWnd->resize(ui.scrollArea->maximumViewportSize());
			}	

			// 捕捉变化后的 viewport 大小,出现滑块后 view相应变小
			QTimer::singleShot(0, [this](){
				qDebug() << "TimerOut" << ui.scrollArea->viewport()->size()
					<< " Area:" << ui.scrollArea->size()<< " displayWnd:" << _disPlayWnd->size()
					<< ui.scrollArea->maximumViewportSize();
			}); 

			return true;
		}
	}

	if (watched == _disPlayWnd && event->type() == QEvent::Wheel)
	{
		QWheelEvent* evt = (QWheelEvent*)event;

		if (evt->delta() > 0)
		{
			zoomIn();
		}
		else
		{
			zoomOut();
		}

		return true;
	}

	return false;
}

void BaseDisParentWnd::customEvent(QEvent *event)
{
	//qApp->postEvent(_disPlayWnd, event);
}

BaseDisParentWnd::~BaseDisParentWnd()
{
	qDebug() << m_strNam << ":BaseDisParentWnd dst";
	_disPlayWnd->setEnabled(false);
	delete _disPlayWnd;
	_disPlayWnd = NULL;
}

// int BaseDisParentWnd::heightForWidth(int width) const
// {
// 	//return 768.0 * width / 1366.0;
// 	//return width;
// 
// 	return 100;
// }

void BaseDisParentWnd::resizeWndToNormal()
{
	QTimer::singleShot(0, this, [this]()
	{
		_disPlayWnd->resize(ui.scrollArea->viewport()->size());
	});
}

QPaintEngine * BaseDisParentWnd::paintEngine() const
{
	throw std::logic_error("The method or operation is not implemented.");
}

void BaseDisParentWnd::zoomIn()
{
	DisplayFrameData *frame_data = _disPlayWnd->getCurrentDisplayData();
	if (frame_data == NULL)
	{
		qDebug() << "getCurrentDisplayData return null";
		return;
	}

	QSize Newsize = _disPlayWnd->size();
	QSize s = ui.scrollArea->size();
	if (Newsize.width() > frame_data->column * 3 && Newsize.width() > s.width() * 4)
	{
		return;
	}

	Newsize *= 1.25;
	// widget 大于 scroll 的size时 scroll会自动调小 viewport 同时 出现滑动块
	_disPlayWnd->resize(Newsize);
	adjustScrollBar(1.25);
}

void BaseDisParentWnd::zoomOut()
{
	QSize Newsize = _disPlayWnd->size();
	QSize s = ui.scrollArea->size();

	if (Newsize.width() == s.width())
	{
		return;
	}

	Newsize *= 0.8;
	if (Newsize.width() <= s.width()  )
	{
		// scroll size == viewportsize 时  滑动条会隐藏
		_disPlayWnd->resize(ui.scrollArea->maximumViewportSize());
	}
	else
	{
		_disPlayWnd->resize(Newsize);
		adjustScrollBar(0.8);
	}
}

void BaseDisParentWnd::zoom11()
{
	DisplayFrameData *frame_data = _disPlayWnd->getCurrentDisplayData();
	if (frame_data == NULL)
	{
		qDebug() << "getCurrentDisplayData return null";
		return;
	}

	_disPlayWnd->resize(QSize(frame_data->column, frame_data->line));
	adjustScrollBar(1);
}

void BaseDisParentWnd::restore()
{
	_disPlayWnd->resize(ui.scrollArea->maximumViewportSize());
	
	return;
}

void BaseDisParentWnd::adjustScrollBar(float factor)
{
	QScrollBar* scrollBar = ui.scrollArea->horizontalScrollBar();
	if (scrollBar->value() != 0)
	{
		scrollBar->setValue(int(factor * scrollBar->value() + ((factor - 1) * scrollBar->pageStep() / 2)));
	}

	scrollBar = ui.scrollArea->verticalScrollBar();
	if (scrollBar->value() != 0)
	{
		scrollBar->setValue(int(factor * scrollBar->value() + ((factor - 1) * scrollBar->pageStep() / 2)));
	}
}
